4.2 ایجاد کمپین تبلیغاتی

معرفی


هنگامی که مراحل قبلی را با موفقیت به پایان رساندید، می‌توانید تاجران خود را فعال کنید تا کمپین‌های خرده‌فروشی حداکثر عملکرد ایجاد کنند و از یادگیری ماشینی برای اتخاذ هوشمندانه‌ترین تصمیم برای ارائه هر آگهی استفاده کنند.

برای درک اینکه تبلیغ‌کنندگان باید به صورت دستی در رابط کاربری Google Ads برای ایجاد کمپین حداکثر عملکرد چه کاری انجام دهند، به ایجاد کمپین حداکثر عملکرد مراجعه کنید. این بخش توضیح می‌دهد که چگونه می‌توانید از Google Ads API برای فعال کردن تبلیغ‌کنندگان خود برای ایجاد کمپین‌های Performance Max برای ارائه تبلیغات خرید استفاده کنید.

فقط عملکردهای اولیه کمپین که بیشترین ارزش را به تاجران شما می‌رسانند در حالی که پیچیدگی اجرا را به حداقل می‌رسانند در این بخش پوشش داده می‌شوند. برای هر کمپین، توصیه می‌کنیم سه گزینه سفارشی‌سازی زیر را در اختیار تاجران خود قرار دهید:

  • نام کمپین: نحوه نمایش کمپین در گزارش.

  • بودجه: کمپین به طور متوسط ​​چقدر باید هزینه کند.

  • هدف گذاری جغرافیایی: جایی که کمپین باید تبلیغات را ارائه دهد.

علاوه بر این، در backend باید چند پارامتر دیگر را که در زیر فهرست شده اند مشخص کنید.

استراتژی پیشنهاد

استراتژی پیشنهاد خودکار مشخص می کند که کمپین در هنگام تنظیم پیشنهادات چه چیزی را به حداکثر می رساند. برای حداکثر عملکرد، دو استراتژی پیشنهادی موجود است:

  • به حداکثر رساندن تبدیل ها : کمپین سعی می کند تعداد تبدیل های به دست آمده (به عنوان مثال، تعداد خرید) را برای بودجه معین به حداکثر برساند.

  • به حداکثر رساندن ارزش تبدیل (توصیه می‌شود): کمپین سعی می‌کند کل ارزش پولی تبدیل‌ها (مثلاً دلارهای صرف شده برای خرید) را برای بودجه معین، همانطور که آن را به عنوان بخشی از تنظیم ردیابی تبدیل تعریف کرده‌اید، به حداکثر برساند.

برای ساده‌سازی پیچیدگی فنی و تجربه برای بازرگانان، توصیه می‌کنیم بدون ROAS هدف ، «بیشینه‌سازی ارزش تبدیل» را از طرف تاجران خود انتخاب کنید. این با آنچه از طریق گزینه تغییر مسیر در دسترس است مطابقت دارد.

گروه های دارایی و گروه های فهرست بندی

گروه‌های دارایی جایی هستند که عناصر خلاقانه کمپین مانند محصولات، تصاویر، ویدئوها ذخیره می‌شوند. در رکن پایه فقط محصولات مرچنت سنتر تحت پوشش قرار می گیرند. بنابراین، شما فقط به یک گروه دارایی در هر کمپین نیاز دارید که می توانید آن را از طرف تاجر در backend ایجاد کنید.

گروه‌های فهرست‌بندی به بازرگانان اجازه می‌دهد محصولات را در یک کمپین به گروه‌هایی تقسیم کنند (مثلاً فقط برای تبلیغ محصولات خاص). این یک ویژگی پیشرفته است که در ستون پایه توصیه نمی شود، اما هر کمپین باید حداقل یک گروه لیست داشته باشد. بنابراین، شما فقط به یک گروه فهرست بندی در هر کمپین نیاز دارید که می توانید آن را از طرف تاجر در backend ایجاد کنید.

راهنمای UX


بخش اول این بخش سناریوی ایجاد اولین کمپین را به عنوان بخشی از جریان ورود به گوگل ادز مورد بحث قرار می دهد. بخش دوم نحوه ایجاد کمپین های اضافی را توضیح می دهد. اگر به بازرگانان اجازه دهید کمپین های خود را به صورت جغرافیایی هدف قرار دهند، این امر به ویژه مرتبط است.

ایجاد کمپین در حین ورود به Google Ads

پس از اتصال حساب Google Ads، از کاربر خواسته می شود تا کمپین حداکثر عملکرد را راه اندازی کند:

  • نام کمپین را می توان با یک نام معنادار از قبل پر کرد، به عنوان مثال، کمپین حداکثر عملکرد برای نام فروشگاه ساز شما .

  • همچنین توصیه می‌کنیم پیش‌نمایشی از شکل ظاهری یک تبلیغ و توضیح مختصری درباره نحوه عملکرد کمپین‌های Performance Max داشته باشید. Google Ads API راهی برای ایجاد پیش‌نمایش آگهی ارائه نمی‌دهد، بنابراین باید خودتان آن را پیاده‌سازی کنید. نیازی نیست که آن را پیکسلی کامل کنید، زیرا ممکن است طراحی نوع تبلیغ تغییر کند.

  • اگر تاجر واجد شرایط دریافت کوپن باشد، باید در اعلان ذکر شود.

  • حتماً در این مرحله از جریان، پیوندی به خط‌مشی‌های تبلیغات خرید نیز ارائه دهید.

  • به صورت اختیاری، می‌توانید به تاجر اجازه دهید کمپین را هدف‌گیری جغرافیایی کند.

هنگامی که تاجر به مرحله تنظیم کمپین می رسد، دو سناریو ممکن است:

  1. صورتحساب تنظیم نشده است. زمانی که یک حساب Google Ads جدید به عنوان بخشی از این جریان ایجاد شده باشد، همیشه این اتفاق می افتد.

  2. صورت‌حساب قبلاً برای حساب Google Ad متصل تنظیم شده است (برای مثال، تاجر از یک حساب Google Ads که قبلاً برای اجرای انواع دیگر کمپین‌ها استفاده کرده بود، دوباره استفاده کرد).

سناریوهای صورت‌حساب شکسته باید قبلاً در مرحله پیوند دادن حساب Google Ads شناسایی شده باشند. تنها تفاوت بین سناریو 1 و 2 رفتار و فراخوان برای اقدام (CTA) اقدام اولیه است:

  • برای سناریوی 1، CTA باید «تنظیم صورت‌حساب» نامیده شود و پاپ‌آپ صورت‌حساب را باز کند. پس از ارائه صورت‌حساب و بسته شدن پنجره بازشو، به مرحله «بازبینی و پایان راه‌اندازی» بروید.

  • برای سناریوی 2، CTA باید «ادامه» نامیده شود و مستقیماً به مرحله «بازبینی و پایان راه‌اندازی» بروید.

در زیر مثالی برای نحوه جستجوی مرحله تنظیم کمپین برای سناریو 1 آورده شده است.

pmax

اطلاعات اضافی باید در مورد نام کمپین، محصولات موجود و بودجه به تاجر ارائه شود. نمونه ای از این که چگونه ممکن است به نظر برسد در زیر آمده است.

pmax2

اگر تاجر بودجه ای کمتر از آستانه توصیه شده برای کشوری که در آن تبلیغ می کند وارد کند، اعلانی با حداقل توصیه بودجه ارائه کنید. پیشنهادی در مورد اینکه چگونه ممکن است به نظر برسد در زیر آمده است.

صورت حساب

پس از اتمام مرحله راه اندازی کمپین و ارائه اطلاعات صورتحساب (در صورت لزوم)، کاربر به مرحله "بازبینی و پایان" می رود. به کاربر این فرصت داده می شود تا تنظیمات کمپین خود را برای آخرین بار قبل از راه اندازی کمپین بررسی کند. UI ممکن است در این مرحله چیزی شبیه به این باشد:

pmax3

پس از خروج از جریان ورود، توصیه می‌کنیم یک پیش‌نمایش درباره اتفاقات بعدی به کاربر ارائه دهید. یک مثال در زیر نشان داده شده است.

اولین_کمپین

پس از خروج از ورود، تاجر باید به صفحه نمای کلی هدایت شود. نمونه ای از این که چگونه ممکن است به نظر برسد در زیر آمده است.

بررسی اجمالی

نمایش کمپین ها و ایجاد کمپین های اضافی

رابط کاربری شما باید بتواند چندین کمپین را نمایش دهد، زیرا ممکن است تاجران کمپین‌های Performance Max موجود داشته باشند یا بخواهند بیش از یک کمپین ایجاد کنند، به خصوص اگر به آنها اجازه هدف‌یابی جغرافیایی را بدهید. نمونه ای از اجازه دادن به تاجر برای هدف گذاری جغرافیایی کمپین های خود در زیر نشان داده شده است. همیشه اطمینان حاصل کنید که هدف گذاری جغرافیایی محدودتر از کشور یا کشورهایی است که تاجر در آن تبلیغ می کند.

pmax4

وقتی یک تاجر بیش از یک کمپین دارد، معیارهای تبلیغات را در صفحه نمای کلی جمع آوری کنید. نمونه ای از این که چگونه ممکن است به نظر برسد در زیر آمده است.

pmax4

کمپین‌های جداگانه را در نمای جزئیات Google Ads فهرست کنید. نمونه ای از این که چگونه ممکن است به نظر برسد در زیر آمده است.

جزئیات

نمای جزئیات Google Ads نیز نقطه شروع پیشنهادی برای ایجاد کمپین های اضافی است. مثالی برای اینکه چگونه این جریان ممکن است به نظر برسد در انیمیشن زیر نشان داده شده است. توجه داشته باشید که انیمیشن از تکمیل اطلاعات بودجه خودداری می کند زیرا این قسمت قبلاً در این راهنما پوشش داده شده است.

انیمیشن

راهنمای فنی


هنگامی که حساب Google Ads را به حساب Merchant Center مرتبط کردید، می توانید با استفاده از این مراحل کمپین حداکثر عملکرد برای خرده فروشی ایجاد کنید:

  1. عملیات بودجه را ایجاد کنید
  2. ایجاد عملیات کمپین
  3. معیارهای کمپین ایجاد کنید
  4. ایجاد گروه دارایی با فیلتر گروه فهرست
  5. نادیده گرفتن اهداف تبدیل
  6. همه عملیات را با هم ارسال کنید

توجه داشته باشید که موارد زیر برای مدیریت کمپین‌های Performance Max اعمال می‌شود:

  • باید حداقل از نسخه 9 Google Ads API استفاده کنید

  • شما باید تمام عملیات جهش را در یک درخواست جهش ارسال کنید. بنابراین، ابتدا باید تمام عملیات جهش را ایجاد کنید و سپس همه آنها را با هم در یک درخواست جهش به Google Ads API ارسال کنید.

  • شما از شناسه‌های موقت در تمام عملیات‌هایی که ایجاد می‌کنید استفاده می‌کنید، زیرا یک درخواست جهش را با همه عملیات‌ها ارسال می‌کنید، و این عملیات‌ها از آن شناسه‌های موقت استفاده می‌کنند تا به Google اطلاع دهند که همه آنها به هم تعلق دارند.

نام را تنظیم کنید

اگر این احتمال وجود دارد که بازرگانان شما مستقیماً به پلتفرم Google Ads دسترسی داشته باشند، ممکن است بخواهید در مورد نام کمپین بیشتر فکر کنید تا مشخص شود که شما، شریک، به طور برنامه‌ریزی کمپین را ایجاد کرده‌اید. این به تاجر نشان می دهد که بهتر است کمپین را از داخل پلتفرم خود ویرایش کنید و نه از پلتفرم Google Ads. به عنوان مثال، حتی اگر به تاجر اجازه دهید نام را سفارشی کند، ممکن است بخواهید " ایجاد شده توسط پلتفرم X " را اضافه یا اضافه کنید.

مرحله 1. عملیات بودجه را ایجاد کنید

نمونه کد را می توان در ایجاد بودجه کمپین پیدا کرد، بنابراین در اینجا فقط مهمترین نکات را برجسته می کنیم:

  • کمپین Performance Max مانند همه کمپین‌های دیگر به بودجه نیاز دارد، اما با محدودیت‌های زیر:

    • بودجه باید دارای یک دوره بودجه روزانه باشد.
    • بودجه قابل تقسیم نیست.
  • روش تحویل برای بودجه استاندارد است. تحویل استاندارد عملکرد تبلیغات مورد انتظار را در طول روز در نظر می گیرد و در به حداکثر رساندن عملکرد در حد متوسط ​​بودجه روزانه شما بهتر است.

  • همانطور که قبلا در این بخش ذکر شد، شما باید از شناسه های موقت در تمام عملیات هایی که ایجاد می کنید استفاده کنید. از این رو، در نمونه کد می بینید که یک _BUDGET_TEMPORARY_ID در نام منبع بودجه تنظیم شده است تا بتواند در مراحل بعدی توسط کمپین به آن ارجاع داده شود.

مرحله 2. استراتژی کمپین و مناقصه را ایجاد کنید

برای یادگیری نحوه ایجاد یک کمپین استاندارد Performance Max می توانید به نمونه کد در Create the Campan and Bidding Strategy مراجعه کنید. در اینجا چند یادداشت اضافی وجود دارد:

  • نمونه کد از Maximize Conversion Value برای استراتژی مناقصه استفاده می کند، این همان چیزی است که ما به شما توصیه می کنیم در ستون پایه استفاده کنید. با به حداکثر رساندن ارزش تبدیل، شما می توانید یک ROAS (بازدهی هزینه تبلیغات) را مشخص کنید. حتی اگر مقدار ROAS هدف را تعیین نکنید، باید آن را تنظیم کنید، مگر اینکه از جاوا استفاده کنید. در جاوا، شما دارید: setMaximizeConversionValue(MaximizeConversionValue.newBuilder().build())

  • برای نسخه‌های آینده، می‌توانید از حداکثر کردن تبدیل به عنوان یکی دیگر از گزینه‌های استراتژی مناقصه، با قابلیت تعیین CPA هدف استفاده کنید.

  • گسترش URL به این معنی است که به کمپین اجازه می دهد تا کل دامنه را هدف قرار دهد، نه فقط URL های مشخص شده در Merchant Center آگهی دهنده. توصیه می کنیم از بسط URL نهایی استفاده کنید تا کمپین از نظر دسترسی و در نهایت عملکرد محدود نباشد. توجه داشته باشید که این بدان معناست که تبلیغات ممکن است به هر صفحه ای در وب سایت تاجر ارائه شود.

  • start_date و end_date اختیاری هستند.

برای کمپین حداکثر عملکرد برای خرده فروشی ، باید یک ShoppingSetting با فیلدهای زیر ایجاد کنید و آن را به کمپین اضافه کنید. این نشان می‌دهد که از کدام حساب Merchant Center می‌خواهید استفاده کنید، همراه با فید محصولی که می‌خواهید در آن حساب استفاده کنید. همانطور که در کمپین خرده فروشی ایجاد حداکثر عملکرد توضیح داده شد، باید این فیلدهای ShoppingSetting را تنظیم کنید:

  • merchant_id (لازم): شناسه Merchant Center حسابی که حاوی محصولات برای تبلیغ است.

  • feed_label (ممکن است لازم باشد): اگر این احتمال وجود دارد که شما تاجری دارید که از یک حساب موجود در Merchant Center با فیدهای محصول قدیمی استفاده می کند، باید از feed_label استفاده کنید تا مشخص کنید که می خواهید کمپین از فید محصولی که ایجاد کرده اید استفاده کند. این به DatafeedTarget در Content API نگاشت می شود.

در اینجا نمونه ای از نحوه عملکرد ایجاد حداکثر عملکرد برای کمپین خرده فروشی و استراتژی مناقصه ارائه شده است. این فرض را بر این می‌گذارد که فقط یک فید محصول در حساب Merchant Center (یعنی همان چیزی که شما مدیریت می‌کنید) وجود دارد، بنابراین feed_label را تنظیم نمی‌کند.

Python
mutate_operation = googleads_client.get_type("MutateOperation")
campaign = mutate_operation.campaign_operation.create
campaign.name = f"Performance Max campaign #{uuid4()}"

campaign.status = googleads_client.enums.CampaignStatusEnum.ENABLED

campaign.advertising_channel_type = (       googleads_client.enums.AdvertisingChannelTypeEnum.PERFORMANCE_MAX
)

# Add Merchant Center ID to create a Performance Max for Retail
# campaign. This ID should be linked to the Ads account
# where you are creating the campaign
campaign.shopping_setting.merchant_id = int(google_merchant_id)
campaign.maximize_conversion_value.target_roas = None

# Set the Final URL expansion opt out. This option is specific to
# Performance Max campaigns. If opted out (True), only the
# final URLs in the asset group or URLs specified in the
# advertiser's Google Merchant Center or business data feeds are
# targeted.
# If opted in (False), the entire domain will be targeted.
# For best results, set this value to false to opt in and allow
# URL expansions. You can optionally add exclusions to limit
# traffic to parts of your website.
campaign.url_expansion_opt_out = False

# Assign the resource name with a temporary ID.
campaign_service = googleads_client.get_service("CampaignService")
campaign.resource_name = campaign_service.campaign_path(
       customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
)

# Set the budget using the given budget resource name.
campaign.campaign_budget = campaign_service.campaign_budget_path(
      customer_id, _BUDGET_TEMPORARY_ID
)

print("mutate_operation for step 2 - campaign and bidding strategy creation")
print(mutate_operation)

return mutate_operation

مرحله 3. معیارهای کمپین را ایجاد کنید

شما می توانید نمونه کد را در Create the Campaign معیارها مشاهده کنید، بنابراین در اینجا مهمترین نکات وجود دارد:

  • همانطور که نمونه کد نشان می دهد، ابتدا باید تمام خدمات مورد نیاز خود را قبل از پیکربندی معیارهای کمپین LOCATION و LANGUAGE شروع کنید.

  • هنگام تنظیم معیارهای کمپین LOCATION، می‌توانید با تنظیم campaign_criterion.negative به True، اهداف منفی را برای مکان‌هایی که می‌خواهید حذف کنید، اضافه کنید.

  • مطمئن شوید که از همان شناسه موقت مرحله 2 استفاده می‌کنید تا به Google بگویید این معیار را برای کدام کمپین ایجاد می‌کنید.

مرحله 4. گروه دارایی را با فیلتر گروه لیست ایجاد کنید

همانطور که در Creating Performance Max Asset Groups توضیح داده شد، یک گروه دارایی مجموعه ای از دارایی ها است که بر یک موضوع متمرکز شده یا به یک مخاطب هدف مرتبط است. گروه دارایی برای جمع آوری تمام تبلیغات شما و ایجاد موجودی برای همه قالب های تبلیغاتی قابل اجرا برای هدف تبلیغاتی شما استفاده می شود.

می‌توانید از گروه‌های فهرست خرید برای تقسیم‌بندی محصولات به گروه‌ها برای گنجاندن مجموعه‌ای از محصولات یا حذف آنها استفاده کنید. به شما امکان می‌دهد مجموعه محصولاتی را که برای نمایش در کمپین واجد شرایط هستند فیلتر کنید.

می‌توانید نمونه کد ایجاد چندین گروه فهرست محصول را در گروه‌های فهرست‌بندی Performance Max ببینید، اما برای ستون پایه، توصیه می‌شود یک کمپین واحد راه‌اندازی کنید که همه محصولات یک تاجر را هدف قرار دهد. از این رو، یک نمونه کد ساده تر در زیر گنجانده شده است. شما باید یک گره ریشه AssetGroupListingGroupFilter شامل همه محصولات را اضافه کنید. در غیر این صورت، تبلیغات ارائه نمی شوند.

Python
googleads_service = googleads_client.get_service("GoogleAdsService")

operations = []

# Create the AssetGroup
mutate_operation = googleads_client.get_type("MutateOperation")
asset_group = mutate_operation.asset_group_operation.create
asset_group.name = f"Performance Max asset group #{uuid4()}"
asset_group.campaign = googleads_service.campaign_path(
       customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
)

asset_group.status = googleads_client.enums.AssetGroupStatusEnum.ENABLED
asset_group.resource_name = googleads_service.asset_group_path(
      customer_id,
      _ASSET_GROUP_TEMPORARY_ID,
)
operations.append(mutate_operation)

# Create a listing group and link it to the AssetGroup.
# The way you link a listing group to an AssetGroup is different
# than when you are linking headlines, descriptions, images, etc
# where you set the field_type of the asset_group_asset as HEADLINE, for example.
# https://developers.google.com/google-ads/api/reference/rpc/v13/AssetGroupListingGroupFilter
mutate_operation = googleads_client.get_type("MutateOperation")
asset_group_listing_group = mutate_operation.asset_group_listing_group_filter_operation.create
asset_group_listing_group.asset_group = googleads_service.asset_group_path(
       customer_id,
       _ASSET_GROUP_TEMPORARY_ID,
)

asset_group_listing_group.type_ = googleads_client.enums.ListingGroupFilterTypeEnum.UNIT_INCLUDED
# here are other options for ListingGroupFilterTypeEnum
# https://developers.google.com/google-ads/api/reference/rpc/v13/ListingGroupFilterTypeEnum.ListingGroupFilterType

asset_group_listing_group.vertical = googleads_client.enums.ListingGroupFilterVerticalEnum.SHOPPING

operations.append(mutate_operation)

print("mutate_operation for step 4 - create asset group and product group attached to that asset group")
print(operations)

return operations

برای بهبود عملکرد کمپین می‌توانید سرفصل‌ها، توضیحات و حتی تصاویر را به گروه دارایی اضافه کنید. با این حال، از آنجایی که این یک الزام برای Performance Max برای خرده فروشی نیست، ما آن را برای ستون بنیادی توصیه نمی کنیم.

مرحله 5. اهداف تبدیل را بازنویسی کنید

همانطور که در بخش ردیابی تبدیل گفته شد، اگر به بازرگانان اجازه می‌دهید از حساب Google Ads موجود استفاده کنند ، اکیداً توصیه می‌کنیم که اهداف تبدیل را در سطح کمپین تعیین کنید. این را می توان با نادیده گرفتن اهداف تبدیل در سطح مشتری یا حساب انجام داد. وقتی هدف تبدیل در سطح کمپین تعیین نشده باشد، کمپین به طور پیش‌فرض روی هدف تبدیل در سطح مشتری قرار می‌گیرد.

نمونه کد را می توان در به روز رسانی اهداف تبدیل کمپین پیدا کرد. ابتدا باید اهداف تبدیل حساب را بدست آورید. سپس می توانید اهداف تبدیل را تکرار کنید و بررسی کنید که آیا هر هدف برای فروش آنلاین است (یعنی طبقه بندی خرید، وب سایت مبدا). اگر اینطور نیست، biddability را روی False تنظیم کنید. اگر چنین است، biddability را روی True تنظیم کنید. این تضمین می کند که کمپین Performance Max برای افزایش فروش آنلاین بهینه شده است.

مرحله 6. همه عملیات جهش را با هم ارسال کنید

در نهایت، شما باید تمام عملیات جهش را با هم ارسال کنید. همانطور که در نمونه کد زیر نشان داده شده است، ترتیب عملیات مهم است، زیرا آنها به یکدیگر بستگی دارند. به عنوان مثال، performance_max_campaign_operation از داده های campaign_budget_operation استفاده می کند، و campaign_criterion_operations از داده های performance_max_campaign_operation استفاده می کند.

Python
ga_service = googleads_client.get_service("GoogleAdsService")
response = ga_service.mutate(
   customer_id=customer_id,
   mutate_operations=[
       # It's important to create the entities in this order
       # because they depend on each other
       campaign_budget_operation,       # Step 1
       performance_max_campaign_operation,  # Step 2
       # Expand the list of multiple operations into the list of
       # other mutate operations
       *campaign_criterion_operations,  # Step 3
       *asset_group_operations,             # Step 4
       *conversion_goal_operations,         # Step 5
   ],
)

پس از اینکه کمپین با موفقیت ایجاد شد، API چیزی شبیه به زیر را برمی گرداند:

Python
mutate_operation_responses {
campaign_budget_result {
resource_name: "customers/7244690427/campaignBudgets/10689184019"
}
}
mutate_operation_responses {
campaign_result {
    resource_name: "customers/7244690427/campaigns/16822128600"
}
}
mutate_operation_responses {
campaign_criterion_result {
        resource_name: "customers/7244690427/campaignCriteria/16822128600~20009"
}
}
mutate_operation_responses {
    campaign_criterion_result {
resource_name: "customers/7244690427/campaignCriteria/16822128600~20024"
    }
}
mutate_operation_responses {
campaign_criterion_result {
        resource_name: "customers/7244690427/campaignCriteria/16822128600~1003"
    }
}
mutate_operation_responses {
asset_group_result {
        resource_name: "customers/7244690427/assetGroups/6444254998"
    }
}
mutate_operation_responses {
asset_group_listing_group_filter_result {
    resource_name: "customers/7244690427/assetGroupListingGroupFilters/6444254998~6454725458"
}
}
mutate_operation_responses {
campaign_conversion_goal_result {
    resource_name: "customers/7244690427/campaignConversionGoals/16822128600~PURCHASE~WEBSITE"
}
}

resource_name کمپین و بودجه را ذخیره کنید زیرا هنگام ویرایش تنظیمات کمپین و برای اهداف گزارش به آنها نیاز دارید.

جاوا

// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.ads.googleads.examples.shoppingads;

import static com.google.ads.googleads.examples.utils.CodeSampleHelper.getPrintableDateTime;

import com.beust.jcommander.Parameter;
import com.google.ads.googleads.examples.utils.ArgumentNames;
import com.google.ads.googleads.examples.utils.CodeSampleParams;
import com.google.ads.googleads.lib.GoogleAdsClient;
import com.google.ads.googleads.lib.utils.FieldMasks;
import com.google.ads.googleads.v17.common.ImageAsset;
import com.google.ads.googleads.v17.common.LanguageInfo;
import com.google.ads.googleads.v17.common.LocationInfo;
import com.google.ads.googleads.v17.common.MaximizeConversionValue;
import com.google.ads.googleads.v17.common.TextAsset;
import com.google.ads.googleads.v17.enums.AdvertisingChannelTypeEnum.AdvertisingChannelType;
import com.google.ads.googleads.v17.enums.AssetFieldTypeEnum.AssetFieldType;
import com.google.ads.googleads.v17.enums.AssetGroupStatusEnum.AssetGroupStatus;
import com.google.ads.googleads.v17.enums.BudgetDeliveryMethodEnum.BudgetDeliveryMethod;
import com.google.ads.googleads.v17.enums.CampaignStatusEnum.CampaignStatus;
import com.google.ads.googleads.v17.enums.ConversionActionCategoryEnum.ConversionActionCategory;
import com.google.ads.googleads.v17.enums.ConversionOriginEnum.ConversionOrigin;
import com.google.ads.googleads.v17.enums.ListingGroupFilterListingSourceEnum.ListingGroupFilterListingSource;
import com.google.ads.googleads.v17.enums.ListingGroupFilterTypeEnum.ListingGroupFilterType;
import com.google.ads.googleads.v17.errors.GoogleAdsError;
import com.google.ads.googleads.v17.errors.GoogleAdsException;
import com.google.ads.googleads.v17.resources.Asset;
import com.google.ads.googleads.v17.resources.AssetGroup;
import com.google.ads.googleads.v17.resources.AssetGroupAsset;
import com.google.ads.googleads.v17.resources.AssetGroupListingGroupFilter;
import com.google.ads.googleads.v17.resources.Campaign;
import com.google.ads.googleads.v17.resources.Campaign.ShoppingSetting;
import com.google.ads.googleads.v17.resources.CampaignBudget;
import com.google.ads.googleads.v17.resources.CampaignConversionGoal;
import com.google.ads.googleads.v17.resources.CampaignCriterion;
import com.google.ads.googleads.v17.resources.CustomerConversionGoal;
import com.google.ads.googleads.v17.services.AssetGroupAssetOperation;
import com.google.ads.googleads.v17.services.AssetGroupListingGroupFilterOperation;
import com.google.ads.googleads.v17.services.AssetGroupOperation;
import com.google.ads.googleads.v17.services.AssetOperation;
import com.google.ads.googleads.v17.services.CampaignBudgetOperation;
import com.google.ads.googleads.v17.services.CampaignConversionGoalOperation;
import com.google.ads.googleads.v17.services.CampaignCriterionOperation;
import com.google.ads.googleads.v17.services.CampaignOperation;
import com.google.ads.googleads.v17.services.GoogleAdsRow;
import com.google.ads.googleads.v17.services.GoogleAdsServiceClient;
import com.google.ads.googleads.v17.services.GoogleAdsServiceClient.SearchPagedResponse;
import com.google.ads.googleads.v17.services.MutateGoogleAdsResponse;
import com.google.ads.googleads.v17.services.MutateOperation;
import com.google.ads.googleads.v17.services.MutateOperationResponse;
import com.google.ads.googleads.v17.utils.ResourceNames;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
import com.google.protobuf.ByteString;
import com.google.protobuf.Descriptors.FieldDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import org.joda.time.DateTime;

/**
 * This example shows how to create a Performance Max retail campaign.
 *
 * <p>This will be created for "All products".
 *
 * <p>For more information about Performance Max retail campaigns, see
 * https://developers.google.com/google-ads/api/docs/performance-max/retail
 *
 * <p>Prerequisites: - You need to have access to a Merchant Center account. You can find
 * instructions to create a Merchant Center account here:
 * https://support.google.com/merchants/answer/188924. This account must be linked to your Google
 * Ads account. The integration instructions can be found at:
 * https://developers.google.com/google-ads/api/docs/shopping-ads/merchant-center - You need your
 * Google Ads account to track conversions. The different ways to track conversions can be found
 * here: https://support.google.com/google-ads/answer/1722054. - You must have at least one
 * conversion action in the account. For more about conversion actions, see
 * https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions
 */
public class AddPerformanceMaxRetailCampaign {

  // We specify temporary IDs that are specific to a single mutate request. Temporary IDs are always
  // negative and unique within one mutate request.
  //
  // <p>See https://developers.google.com/google-ads/api/docs/mutating/best-practices for further
  // details.
  //
  // <p>These temporary IDs are fixed because they are used in multiple places.
  private static final int BUDGET_TEMPORARY_ID = -1;
  private static final int PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = -2;
  private static final int ASSET_GROUP_TEMPORARY_ID = -3;

  // There are also entities that will be created in the same request but do not
  // need to be fixed temporary IDs because they are referenced only once.
  private static long temporaryId = ASSET_GROUP_TEMPORARY_ID - 1;

  private static class AddPerformanceMaxRetailCampaignParams extends CodeSampleParams {

    @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)
    private Long customerId;

    @Parameter(
        names = ArgumentNames.MERCHANT_CENTER_ACCOUNT_ID,
        required = true,
        description = "The Merchant Center account ID.")
    private long merchantCenterAccountId;

    @Parameter(
        names = ArgumentNames.SALES_COUNTRY,
        description = "The sales country of products to include in the campaign.")
    private String salesCountry = "US";

    @Parameter(
        names = ArgumentNames.FINAL_URL,
        required = true,
        description =
            "The final url for the generated ads. Must have the same domain as the Merchant Center"
                + " account.")
    private String finalUrl;
  }

  public static void main(String[] args) throws IOException {
    AddPerformanceMaxRetailCampaignParams params = new AddPerformanceMaxRetailCampaignParams();
    if (!params.parseArguments(args)) {

      // Either pass the required parameters for this example on the command line, or insert them
      // into the code here. See the parameter class definition above for descriptions.
      params.customerId = Long.parseLong("INSERT_CUSTOMER_ID_HERE");
      params.merchantCenterAccountId = Long.parseLong("INSERT_MERCHANT_CENTER_ACCOUNT_ID_HERE");
      params.finalUrl = "INSERT_FINAL_URL_HERE";

      // Optionally set the sales country.
      // params.salesCountry = "INSERT_SALES_COUNTRY_HERE";
    }

    GoogleAdsClient googleAdsClient = null;
    try {
      googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();
    } catch (FileNotFoundException fnfe) {
      System.err.printf(
          "Failed to load GoogleAdsClient configuration from file. Exception: %s%n", fnfe);
      System.exit(1);
    } catch (IOException ioe) {
      System.err.printf("Failed to create GoogleAdsClient. Exception: %s%n", ioe);
      System.exit(1);
    }

    try {
      new AddPerformanceMaxRetailCampaign()
          .runExample(
              googleAdsClient,
              params.customerId,
              params.merchantCenterAccountId,
              params.salesCountry,
              params.finalUrl);
    } catch (GoogleAdsException gae) {
      // GoogleAdsException is the base class for most exceptions thrown by an API request.
      // Instances of this exception have a message and a GoogleAdsFailure that contains a
      // collection of GoogleAdsErrors that indicate the underlying causes of the
      // GoogleAdsException.
      System.err.printf(
          "Request ID %s failed due to GoogleAdsException. Underlying errors:%n",
          gae.getRequestId());
      int i = 0;
      for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {
        System.err.printf("  Error %d: %s%n", i++, googleAdsError);
      }
      System.exit(1);
    }
  }

  /**
   * Runs the example.
   *
   * @param googleAdsClient the Google Ads API client.
   * @param customerId the client customer ID.
   * @param merchantCenterAccountId the Merchant Center account ID.
   * @param salesCountry sales country of products to include in the campaign.
   * @param finalUrl final URL for the asset group of the campaign.
   */
  private void runExample(
      GoogleAdsClient googleAdsClient,
      long customerId,
      long merchantCenterAccountId,
      String salesCountry,
      String finalUrl)
      throws IOException {
    // This campaign will override the customer conversion goals. For more information see
    // https://developers.google.com/google-ads/api/docs/conversions/goals/campaign-goals.
    // Retrieve the current list of customer conversion goals.
    List<CustomerConversionGoal> customerConversionGoals =
        getCustomerConversionGoals(googleAdsClient, customerId);

    // Performance Max campaigns require that repeated assets such as headlines
    // and descriptions be created before the campaign.
    // For the list of required assets for a Performance Max campaign, see
    // https://developers.google.com/google-ads/api/docs/performance-max/assets
    //
    // Creates the headlines.
    List<String> headlines = ImmutableList.of("Travel", "Travel Reviews", "Book travel");
    List<String> headlineAssetResourceNames =
        createMultipleTextAssets(googleAdsClient, customerId, headlines);
    // Creates the descriptions.
    List<String> descriptions = ImmutableList.of("Take to the air!", "Fly to the sky!");
    List<String> descriptionAssetResourceNames =
        createMultipleTextAssets(googleAdsClient, customerId, descriptions);

    // The below methods create and return MutateOperations that we later
    // provide to the GoogleAdsService.Mutate method in order to create the
    // entities in a single request. Since the entities for a Performance Max
    // campaign are closely tied to one-another, it's considered a best practice
    // to create them in a single Mutate request, so they all complete
    // successfully or fail entirely, leaving no orphaned entities. See:
    // https://developers.google.com/google-ads/api/docs/mutating/overview
    List<MutateOperation> mutateOperations = new ArrayList<>();
    mutateOperations.add(createCampaignBudgetOperation(customerId));
    mutateOperations.add(
        createPerformanceMaxCampaignOperation(customerId, merchantCenterAccountId, salesCountry));
    mutateOperations.addAll(createCampaignCriterionOperations(customerId));
    String assetGroupResourceName = ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID);
    mutateOperations.add(createAssetGroupOperation(customerId, assetGroupResourceName, finalUrl));
    // Retail Performance Max campaigns require listing groups, which are created via the
    // AssetGroupListingGroupFilter resource.
    mutateOperations.add(createAssetGroupListingGroupFilterOperation(assetGroupResourceName));
    mutateOperations.addAll(
        createAssetAndAssetGroupAssetOperations(
            customerId,
            assetGroupResourceName,
            headlineAssetResourceNames,
            descriptionAssetResourceNames));
    mutateOperations.addAll(createConversionGoalOperations(customerId, customerConversionGoals));

    try (GoogleAdsServiceClient googleAdsServiceClient =
        googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {
      MutateGoogleAdsResponse response =
          googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations);
      printResponseDetails(response);
    }
  }

  /** Creates a MutateOperation that creates a new CampaignBudget. */
  private MutateOperation createCampaignBudgetOperation(long customerId) {
    CampaignBudget campaignBudget =
        CampaignBudget.newBuilder()
            .setName("Performance Max retail campaign budget #" + getPrintableDateTime())
            // The budget period already defaults to DAILY.
            .setAmountMicros(50_000_000)
            .setDeliveryMethod(BudgetDeliveryMethod.STANDARD)
            // A Performance Max campaign cannot use a shared campaign budget.
            .setExplicitlyShared(false)
            // Set a temporary ID in the budget's resource name, so it can be referenced
            // by the campaign in later steps.
            .setResourceName(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID))
            .build();

    return MutateOperation.newBuilder()
        .setCampaignBudgetOperation(
            CampaignBudgetOperation.newBuilder().setCreate(campaignBudget).build())
        .build();
  }


  /** Creates a MutateOperation that creates a new Performance Max campaign. */
  private MutateOperation createPerformanceMaxCampaignOperation(
      long customerId, long merchantCenterAccountId, String salesCountry) {
    Campaign performanceMaxCampaign =
        Campaign.newBuilder()
            .setName("Performance Max retail campaign #" + getPrintableDateTime())
            // Sets the campaign status as PAUSED. The campaign is the only entity in
            // the mutate request that should have its status set.
            .setStatus(CampaignStatus.PAUSED)
            // All Performance Max campaigns have an advertising_channel_type of
            // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.
            .setAdvertisingChannelType(AdvertisingChannelType.PERFORMANCE_MAX)
            // Bidding strategy must be set directly on the campaign.
            // Setting a portfolio bidding strategy by resource name is not supported.
            // Max Conversion and Maximize Conversion Value are the only strategies
            // supported for Performance Max campaigns.
            // An optional ROAS (Return on Advertising Spend) can be set for
            // maximize_conversion_value. The ROAS value must be specified as a ratio in
            // the API. It is calculated by dividing "total value" by "total spend".
            // For more information on Maximize Conversion Value, see the support
            // article: http://support.google.com/google-ads/answer/7684216.
            // A targetRoas of 3.5 corresponds to a 350% return on ad spend.
            // For first time users, it's recommended not to set a target ROAS value. Although
            // the target ROAS value is optional, you still need to define the enclosing
            // maximize_conversion_value.
            .setMaximizeConversionValue(
                MaximizeConversionValue.newBuilder()
                    // .setTargetRoas(3.5)
                    .build())
            // Below is what you would use if you want to maximize conversions:
            // .setMaximizeConversions(
            //     MaximizeConversions.newBuilder()
            //         // The target CPA is optional. This is the average amount that you would like
            //         // to spend per conversion action.
            //         // .setTargetCpaMicros(1_000_000)
            //         .build())
            // Sets the shopping settings.
            .setShoppingSetting(
                ShoppingSetting.newBuilder()
                    .setMerchantId(merchantCenterAccountId)
                    // Optional: To use products only from a specific feed, set FeedLabel to the
                    // feed label used in Merchant Center. See:
                    // https://support.google.com/merchants/answer/12453549.
                    // Removing the feedLabel field will use products from all feeds.
                    // .setFeedLabel("INSERT_FEED_LABEL_HERE")
                    .build())
            // Sets the Final URL expansion opt out. This flag is specific to
            // Performance Max campaigns. If opted out (true), only the final URLs in
            // the asset group or URLs specified in the advertiser's Google Merchant
            // Center or business data feeds are targeted.
            //
            // If opted in (false), the entire domain will be targeted. For best
            // results, set this value to false to opt in and allow URL expansions. You
            // can optionally add exclusions to limit traffic to parts of your website.
            //
            // Sets to true for this Retail campaign so the final URLs will be limited to those
            // explicitly surfaced via Google Merchant Center.
            .setUrlExpansionOptOut(true)
            // Assigns the resource name with a temporary ID.
            .setResourceName(
                ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID))
            // Sets the budget using the given budget resource name.
            .setCampaignBudget(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID))
            // Optional fields.
            .setStartDate(new DateTime().plusDays(1).toString("yyyyMMdd"))
            .setEndDate(new DateTime().plusDays(365).toString("yyyyMMdd"))
            .build();

    return MutateOperation.newBuilder()
        .setCampaignOperation(
            CampaignOperation.newBuilder().setCreate(performanceMaxCampaign).build())
        .build();
  }


  /** Creates a list of MutateOperations that create new campaign criteria. */
  private List<MutateOperation> createCampaignCriterionOperations(long customerId) {
    String campaignResourceName =
        ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID);
    List<CampaignCriterion> campaignCriteria = new ArrayList<>();
    // Sets the LOCATION campaign criteria.
    // Targets all of New York City except Brooklyn.
    // Location IDs are listed here:
    // https://developers.google.com/google-ads/api/reference/data/geotargets
    // and they can also be retrieved using the GeoTargetConstantService as shown
    // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting
    campaignCriteria.add(
        CampaignCriterion.newBuilder()
            .setCampaign(campaignResourceName)
            // Adds one positive location target for New York City (ID=1023191), specifically adding
            // the positive criteria before the negative one.
            .setLocation(
                LocationInfo.newBuilder()
                    .setGeoTargetConstant(ResourceNames.geoTargetConstant(1023191))
                    .build())
            .setNegative(false)
            .build());
    // Next adds the negative target for Brooklyn (ID=1022762).
    campaignCriteria.add(
        CampaignCriterion.newBuilder()
            .setCampaign(campaignResourceName)
            .setLocation(
                LocationInfo.newBuilder()
                    .setGeoTargetConstant(ResourceNames.geoTargetConstant(1022762))
                    .build())
            .setNegative(true)
            .build());
    // Sets the LANGUAGE campaign criterion.
    campaignCriteria.add(
        CampaignCriterion.newBuilder()
            .setCampaign(campaignResourceName)
            // Sets the language.
            // For a list of all language codes, see:
            // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7
            .setLanguage(
                LanguageInfo.newBuilder()
                    .setLanguageConstant(ResourceNames.languageConstant(1000)) // English
                    .build())
            .build());
    // Returns a list of mutate operations with one operation per criterion.
    return campaignCriteria.stream()
        .map(
            criterion ->
                MutateOperation.newBuilder()
                    .setCampaignCriterionOperation(
                        CampaignCriterionOperation.newBuilder().setCreate(criterion).build())
                    .build())
        .collect(Collectors.toList());
  }


  /**
   * Creates multiple text assets and returns the list of resource names.
   *
   * <p>These repeated assets must be created in a separate request prior to creating the campaign.
   */
  private List<String> createMultipleTextAssets(
      GoogleAdsClient googleAdsClient, long customerId, List<String> texts) {
    List<MutateOperation> mutateOperations = new ArrayList<>();
    for (String text : texts) {
      Asset asset = Asset.newBuilder().setTextAsset(TextAsset.newBuilder().setText(text)).build();
      AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build();
      mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build());
    }

    List<String> assetResourceNames = new ArrayList<>();
    // Creates the service client.
    try (GoogleAdsServiceClient googleAdsServiceClient =
        googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {
      // Sends the operations in a single Mutate request.
      MutateGoogleAdsResponse response =
          googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations);
      for (MutateOperationResponse result : response.getMutateOperationResponsesList()) {
        if (result.hasAssetResult()) {
          assetResourceNames.add(result.getAssetResult().getResourceName());
        }
      }
      printResponseDetails(response);
    }
    return assetResourceNames;
  }


  /** Creates a MutateOperation that create a new AssetGroup. */
  private MutateOperation createAssetGroupOperation(
      long customerId, String assetGroupResourceName, String finalUrl) {
    List<MutateOperation> mutateOperations = new ArrayList<>();
    String campaignResourceName =
        ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID);
    // Creates the AssetGroup.
    AssetGroup assetGroup =
        AssetGroup.newBuilder()
            .setName("Performance Max retail asset group #" + getPrintableDateTime())
            .setCampaign(campaignResourceName)
            .addFinalUrls(finalUrl)
            .addFinalMobileUrls(finalUrl)
            .setStatus(AssetGroupStatus.PAUSED)
            .setResourceName(assetGroupResourceName)
            .build();
    AssetGroupOperation assetGroupOperation =
        AssetGroupOperation.newBuilder().setCreate(assetGroup).build();
    return MutateOperation.newBuilder().setAssetGroupOperation(assetGroupOperation).build();
  }


  /** Creates a list of MutateOperations that create a new AssetGroup. */
  private List<MutateOperation> createAssetAndAssetGroupAssetOperations(
      long customerId,
      String assetGroupResourceName,
      List<String> headlineAssetResourceNames,
      List<String> descriptionAssetResourceNames)
      throws IOException {
    List<MutateOperation> mutateOperations = new ArrayList<>();

    // For the list of required assets for a Performance Max campaign, see
    // https://developers.google.com/google-ads/api/docs/performance-max/assets

    // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset
    // and providing:
    //   the resource name of the AssetGroup
    //   the resource name of the Asset
    //   the field_type of the Asset in this AssetGroup.

    // To learn more about AssetGroups, see
    // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups

    // Links the previously created multiple text assets.

    // Links the headline assets.
    for (String resourceName : headlineAssetResourceNames) {
      AssetGroupAsset assetGroupAsset =
          AssetGroupAsset.newBuilder()
              .setFieldType(AssetFieldType.HEADLINE)
              .setAssetGroup(assetGroupResourceName)
              .setAsset(resourceName)
              .build();
      AssetGroupAssetOperation assetGroupAssetOperation =
          AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build();
      mutateOperations.add(
          MutateOperation.newBuilder()
              .setAssetGroupAssetOperation(assetGroupAssetOperation)
              .build());
    }

    // Links the description assets.
    for (String resourceName : descriptionAssetResourceNames) {
      AssetGroupAsset assetGroupAsset =
          AssetGroupAsset.newBuilder()
              .setFieldType(AssetFieldType.DESCRIPTION)
              .setAssetGroup(assetGroupResourceName)
              .setAsset(resourceName)
              .build();
      AssetGroupAssetOperation assetGroupAssetOperation =
          AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build();
      mutateOperations.add(
          MutateOperation.newBuilder()
              .setAssetGroupAssetOperation(assetGroupAssetOperation)
              .build());
    }

    // Creates and links the long headline text asset.
    mutateOperations.addAll(
        createAndLinkTextAsset(
            customerId, assetGroupResourceName, "Travel the World", AssetFieldType.LONG_HEADLINE));

    // Creates and links the business name text asset.
    mutateOperations.addAll(
        createAndLinkTextAsset(
            customerId,
            assetGroupResourceName,
            "Interplanetary Cruises",
            AssetFieldType.BUSINESS_NAME));

    // Creates and links the image assets.

    // Creates and links the Logo Asset.
    mutateOperations.addAll(
        createAndLinkImageAsset(
            customerId,
            assetGroupResourceName,
            "https://gaagl.page.link/1Crm",
            AssetFieldType.LOGO,
            "Logo Image"));

    // Creates and links the Marketing Image Asset.
    mutateOperations.addAll(
        createAndLinkImageAsset(
            customerId,
            assetGroupResourceName,
            "https://gaagl.page.link/Eit5",
            AssetFieldType.MARKETING_IMAGE,
            "Marketing Image"));

    // Creates and links the Square Marketing Image Asset.
    mutateOperations.addAll(
        createAndLinkImageAsset(
            customerId,
            assetGroupResourceName,
            "https://gaagl.page.link/bjYi",
            AssetFieldType.SQUARE_MARKETING_IMAGE,
            "Square Marketing Image"));

    return sortAssetAndAssetGroupAssetOperations(mutateOperations);
  }


  /** Creates a list of MutateOperations that create a new linked text asset. */
  List<MutateOperation> createAndLinkTextAsset(
      long customerId, String assetGroupResourceName, String text, AssetFieldType assetFieldType) {
    List<MutateOperation> mutateOperations = new ArrayList<>();
    String assetResourceName = ResourceNames.asset(customerId, getNextTemporaryId());
    // Creates the Text Asset.
    Asset asset =
        Asset.newBuilder()
            .setResourceName(assetResourceName)
            .setTextAsset(TextAsset.newBuilder().setText(text).build())
            .build();
    AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build();
    mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build());

    // Creates an AssetGroupAsset to link the Asset to the AssetGroup.
    AssetGroupAsset assetGroupAsset =
        AssetGroupAsset.newBuilder()
            .setFieldType(assetFieldType)
            .setAssetGroup(assetGroupResourceName)
            .setAsset(assetResourceName)
            .build();
    AssetGroupAssetOperation assetGroupAssetOperation =
        AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build();
    mutateOperations.add(
        MutateOperation.newBuilder().setAssetGroupAssetOperation(assetGroupAssetOperation).build());

    return mutateOperations;
  }


  /** Creates a list of MutateOperations that create a new linked image asset. */
  List<MutateOperation> createAndLinkImageAsset(
      long customerId,
      String assetGroupResourceName,
      String url,
      AssetFieldType assetFieldType,
      String assetName)
      throws IOException {
    List<MutateOperation> mutateOperations = new ArrayList<>();
    String assetResourceName = ResourceNames.asset(customerId, getNextTemporaryId());
    // Creates a media file.
    byte[] assetBytes = ByteStreams.toByteArray(new URL(url).openStream());

    // Creates the Image Asset.
    Asset asset =
        Asset.newBuilder()
            .setResourceName(assetResourceName)
            .setImageAsset(ImageAsset.newBuilder().setData(ByteString.copyFrom(assetBytes)).build())
            // Provides a unique friendly name to identify your asset. When there is an existing
            // image asset with the same content but a different name, the new name will be dropped
            // silently.
            .setName(assetName)
            .build();
    AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build();
    mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build());

    // Creates an AssetGroupAsset to link the Asset to the AssetGroup.
    AssetGroupAsset assetGroupAsset =
        AssetGroupAsset.newBuilder()
            .setFieldType(assetFieldType)
            .setAssetGroup(assetGroupResourceName)
            .setAsset(assetResourceName)
            .build();
    AssetGroupAssetOperation assetGroupAssetOperation =
        AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build();
    mutateOperations.add(
        MutateOperation.newBuilder().setAssetGroupAssetOperation(assetGroupAssetOperation).build());

    return mutateOperations;
  }


  /**
   * Sorts a list of asset and asset group asset operations.
   *
   * <p>This sorts the list such that all asset operations precede all asset group asset operations.
   * If asset group assets are created before assets then an error will be returned by the API.
   */
  private List<MutateOperation> sortAssetAndAssetGroupAssetOperations(
      List<MutateOperation> operations) {
    List<MutateOperation> sortedOperations =
        operations.stream().filter(o -> o.hasAssetOperation()).collect(Collectors.toList());
    sortedOperations.addAll(
        operations.stream()
            .filter(o -> o.hasAssetGroupAssetOperation())
            .collect(Collectors.toList()));
    return sortedOperations;
  }


  /** Retrieves the list of customer conversion goals. */
  private static List<CustomerConversionGoal> getCustomerConversionGoals(
      GoogleAdsClient googleAdsClient, long customerId) {
    String query =
        "SELECT customer_conversion_goal.category, customer_conversion_goal.origin "
            + "FROM customer_conversion_goal";

    List<CustomerConversionGoal> customerConversionGoals = new ArrayList<>();
    try (GoogleAdsServiceClient googleAdsServiceClient =
        googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {
      // The number of conversion goals is typically less than 50, so we use
      // GoogleAdsService.search instead of search_stream.
      SearchPagedResponse response =
          googleAdsServiceClient.search(Long.toString(customerId), query);
      for (GoogleAdsRow googleAdsRow : response.iterateAll()) {
        customerConversionGoals.add(googleAdsRow.getCustomerConversionGoal());
      }
    }

    return customerConversionGoals;
  }

  /** Creates a list of MutateOperations that override customer conversion goals. */
  private static List<MutateOperation> createConversionGoalOperations(
      long customerId, List<CustomerConversionGoal> customerConversionGoals) {
    List<MutateOperation> mutateOperations = new ArrayList<>();
    // To override the customer conversion goals, we will change the
    // biddability of each of the customer conversion goals so that only
    // the desired conversion goal is biddable in this campaign.
    for (CustomerConversionGoal customerConversionGoal : customerConversionGoals) {
      ConversionActionCategory category = customerConversionGoal.getCategory();
      ConversionOrigin origin = customerConversionGoal.getOrigin();
      String campaignConversionGoalResourceName =
          ResourceNames.campaignConversionGoal(
              customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID, category, origin);
      CampaignConversionGoal.Builder campaignConversionGoalBuilder =
          CampaignConversionGoal.newBuilder().setResourceName(campaignConversionGoalResourceName);
      // Change the biddability for the campaign conversion goal.
      // Set biddability to True for the desired (category, origin).
      // Set biddability to False for all other conversion goals.
      // Note:
      //  1- It is assumed that this Conversion Action
      //     (category=PURCHASE, origin=WEBSITE) exists in this account.
      //  2- More than one goal can be biddable if desired. This example
      //     shows only one.
      if (category == ConversionActionCategory.PURCHASE && origin == ConversionOrigin.WEBSITE) {
        campaignConversionGoalBuilder.setBiddable(true);
      } else {
        campaignConversionGoalBuilder.setBiddable(false);
      }
      CampaignConversionGoal campaignConversionGoal = campaignConversionGoalBuilder.build();
      CampaignConversionGoalOperation campaignConversionGoalOperation =
          CampaignConversionGoalOperation.newBuilder()
              .setUpdate(campaignConversionGoal)
              .setUpdateMask(FieldMasks.allSetFieldsOf(campaignConversionGoal))
              .build();
      mutateOperations.add(
          MutateOperation.newBuilder()
              .setCampaignConversionGoalOperation(campaignConversionGoalOperation)
              .build());
    }
    return mutateOperations;
  }


  /** Creates a MutateOperation that creates a new asset group listing group filter. */
  private MutateOperation createAssetGroupListingGroupFilterOperation(
      String assetGroupResourceName) {

    // Creates a new asset group listing group filter containing the "default" listing group (All
    // products).
    AssetGroupListingGroupFilter listingGroupFilter =
        AssetGroupListingGroupFilter.newBuilder()
            .setAssetGroup(assetGroupResourceName)
            // Does not set the parentListingGroupFilter since this is the root node. For all other
            // nodes, this would refer to the parent listing group filter resource name.
            // .setParentListingGroupFilter("<PARENT FILTER RESOURCE NAME>")

            // Sets the type to UNIT_INCLUDED since this node has no children.
            .setType(ListingGroupFilterType.UNIT_INCLUDED)
            // Specifies that this uses the SHOPPING listing source, as required for a Performance
            // Max retail campaign.
            .setListingSource(ListingGroupFilterListingSource.SHOPPING)
            .build();

    // Returns an operation to the list to create the listing group filter.
    return MutateOperation.newBuilder()
        .setAssetGroupListingGroupFilterOperation(
            AssetGroupListingGroupFilterOperation.newBuilder().setCreate(listingGroupFilter))
        .build();
  }


  /**
   * Prints the details of a MutateGoogleAdsResponse.
   *
   * <p>Parses the "response" oneof field name and uses it to extract the new entity's name and
   * resource name.
   */
  private void printResponseDetails(MutateGoogleAdsResponse response) {
    // Parses the Mutate response to print details about the entities that were created by the
    // request.
    String suffix = "_result";
    for (MutateOperationResponse result : response.getMutateOperationResponsesList()) {
      for (Entry<FieldDescriptor, Object> responseFields : result.getAllFields().entrySet()) {
        String fieldName = responseFields.getKey().getName();
        String value = responseFields.getValue().toString().trim();
        if (fieldName.endsWith(suffix)) {
          fieldName = fieldName.substring(0, fieldName.length() - suffix.length());
        }
        System.out.printf("Created a(n) %s with %s.%n", fieldName, value);
      }
    }
  }

  /** Returns the next temporary ID and decreases it by one. */
  private long getNextTemporaryId() {
    return temporaryId--;
  }
}

      

سی شارپ

// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using CommandLine;
using Google.Ads.Gax.Examples;
using Google.Ads.Gax.Util;
using Google.Ads.GoogleAds.Config;
using Google.Ads.GoogleAds.Lib;
using Google.Ads.GoogleAds.V17.Common;
using Google.Ads.GoogleAds.V17.Errors;
using Google.Ads.GoogleAds.V17.Resources;
using Google.Ads.GoogleAds.V17.Services;
using Google.Api.Gax;
using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Threading;
using static Google.Ads.GoogleAds.V17.Enums.AdvertisingChannelTypeEnum.Types;
using static Google.Ads.GoogleAds.V17.Enums.AssetFieldTypeEnum.Types;
using static Google.Ads.GoogleAds.V17.Enums.AssetGroupStatusEnum.Types;
using static Google.Ads.GoogleAds.V17.Enums.BudgetDeliveryMethodEnum.Types;
using static Google.Ads.GoogleAds.V17.Enums.CampaignStatusEnum.Types;
using static Google.Ads.GoogleAds.V17.Enums.ConversionActionCategoryEnum.Types;
using static Google.Ads.GoogleAds.V17.Enums.ConversionOriginEnum.Types;
using static Google.Ads.GoogleAds.V17.Enums.ListingGroupFilterListingSourceEnum.Types;
using static Google.Ads.GoogleAds.V17.Enums.ListingGroupFilterTypeEnum.Types;
using static Google.Ads.GoogleAds.V17.Resources.Campaign.Types;

namespace Google.Ads.GoogleAds.Examples.V17
{
    /// <summary>
    /// This example shows how to create a Performance Max retail campaign.
    ///
    /// This will be created for "All products".
    ///
    /// For more information about Performance Max retail campaigns, see
    /// https://developers.google.com/google-ads/api/docs/performance-max/retail
    ///
    /// Prerequisites:
    /// - You need to have access to a Merchant Center account. You can find
    ///   instructions to create a Merchant Center account here:
    ///   https://support.google.com/merchants/answer/188924.
    ///   This account must be linked to your Google Ads account. The integration
    ///   instructions can be found at:
    ///   https://developers.google.com/google-ads/api/docs/shopping-ads/merchant-center
    /// - You need your Google Ads account to track conversions. The different ways
    ///   to track conversions can be found here:
    ///   https://support.google.com/google-ads/answer/1722054.
    /// - You must have at least one conversion action in the account. For
    ///   more about conversion actions, see
    ///   https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions
    /// </summary>
    public class AddPerformanceMaxRetailCampaign : ExampleBase
    {
        /// <summary>
        /// Command line options for running the <see cref="AddPerformanceMaxRetailCampaign"/>
        /// example.
        /// </summary>
        public class Options : OptionsBase
        {
            /// <summary>
            /// The Google Ads customer ID.
            /// </summary>
            [Option("customerId", Required = true, HelpText =
                "The Google Ads customer ID.")]
            public long CustomerId { get; set; }

            /// <summary>
            /// The Merchant Center account ID.
            /// </summary>
            [Option("merchantCenterAccountId", Required = true, HelpText =
                "The Merchant Center account ID.")]
            public long MerchantCenterAccountId { get; set; }

            /// <summary>
            /// The final url for the generated ads. Must have the same domain as the Merchant
            /// Center account.
            /// </summary>
            [Option("finalUrl", Required = true, HelpText =
                "The final url for the generated ads." +
                "Must have the same domain as the Merchant Center account.")]
            public string FinalUrl { get; set; }
        }

        /// <summary>
        /// Main method, to run this code example as a standalone application.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static void Main(string[] args)
        {
            Options options = ExampleUtilities.ParseCommandLine<Options>(args);

            AddPerformanceMaxRetailCampaign codeExample = new AddPerformanceMaxRetailCampaign();
            Console.WriteLine(codeExample.Description);
            codeExample.Run(
                new GoogleAdsClient(),
                options.CustomerId,
                options.MerchantCenterAccountId,
                options.FinalUrl
            );
        }

        // We specify temporary IDs that are specific to a single mutate request. Temporary IDs are
        // always negative and unique within one mutate request.
        //
        // See https://developers.google.com/google-ads/api/docs/mutating/best-practices for further
        // details.
        //
        // These temporary IDs are fixed because they are used in multiple places.
        private const int TEMPORARY_ID_BUDGET = -1;

        private const int TEMPORARY_ID_CAMPAIGN = -2;
        private const int TEMPORARY_ID_ASSET_GROUP = -3;

        // There are also entities that will be created in the same request but do not need to be
        // fixed temporary IDs because they are referenced only once.
        private class AssetGroupAssetTemporaryResourceNameGenerator
        {
            private long customerId;
            private long next;

            public AssetGroupAssetTemporaryResourceNameGenerator(long customerId, long assetGroupId)
            {
                this.customerId = customerId;
                this.next = assetGroupId - 1;
            }

            public string Next()
            {
                long i = next;
                Interlocked.Decrement(ref next);
                return ResourceNames.Asset(customerId, i);
            }
        }

        /// <summary>
        /// Returns a description about the code example.
        /// </summary>
        public override string Description =>
            "This example shows how to create a Performance Max retail campaign.";

        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="client">The Google Ads client.</param>
        /// <param name="customerId">The Google Ads customer ID.</param>
        /// <param name="merchantCenterAccountId">The Merchant Center account ID.</param>
        /// <param name="finalUrl">The final URL.</param>
        public void Run(
                GoogleAdsClient client,
                long customerId,
                long merchantCenterAccountId,
                string finalUrl)
        {
            try
            {
                GoogleAdsServiceClient googleAdsServiceClient =
                client.GetService(Services.V17.GoogleAdsService);

                // This campaign will override the customer conversion goals.
                // Retrieve the current list of customer conversion goals.
                List<CustomerConversionGoal> customerConversionGoals =
                    GetCustomerConversionGoals(client, customerId);

                // Performance Max campaigns require that repeated assets such as headlines and
                // descriptions be created before the campaign.
                //
                // For the list of required assets for a Performance Max campaign, see
                // https://developers.google.com/google-ads/api/docs/performance-max/assets
                //
                // Create the headlines.
                List<string> headlineAssetResourceNames = CreateMultipleTextAssets(
                    client,
                    customerId,
                    new[] {
                    "Travel",
                    "Travel Reviews",
                    "Book travel"
                    }
                );

                // Create the descriptions.
                List<string> descriptionAssetResourceNames = CreateMultipleTextAssets(
                    client,
                    customerId,
                    new[] {
                    "Take to the air!",
                    "Fly to the sky!"
                    }
                );

                string tempResourceNameCampaignBudget = ResourceNames.CampaignBudget(
                    customerId,
                    TEMPORARY_ID_BUDGET
                );

                string assetGroupResourceName = ResourceNames.AssetGroup(
                    customerId,
                    TEMPORARY_ID_ASSET_GROUP
                );

                // The below methods create and return MutateOperations that we later provide to the
                // GoogleAdsService.Mutate method in order to create the entities in a single request.
                // Since the entities for a Performance Max campaign are closely tied to one-another,
                // it's considered a best practice to create them in a single Mutate request so they all
                // complete successfully or fail entirely, leaving no orphaned entities.
                //
                // See: https://developers.google.com/google-ads/api/docs/mutating/overview
                MutateOperation campaignBudgetOperation = CreateCampaignBudgetOperation(
                    tempResourceNameCampaignBudget
                );

                string tempResourceNameCampaign = ResourceNames.Campaign(
                    customerId,
                    TEMPORARY_ID_CAMPAIGN
                );

                MutateOperation performanceMaxCampaignOperation =
                    CreatePerformanceMaxCampaignOperation(
                            tempResourceNameCampaign,
                            tempResourceNameCampaignBudget,
                            merchantCenterAccountId
                        );

                List<MutateOperation> campaignCriterionOperations =
                    CreateCampaignCriterionOperations(tempResourceNameCampaign);

                List<MutateOperation> assetGroupOperations =
                    CreateAssetGroupOperations(
                        tempResourceNameCampaign,
                        assetGroupResourceName,
                        finalUrl,
                        headlineAssetResourceNames,
                        descriptionAssetResourceNames,
                        new AssetGroupAssetTemporaryResourceNameGenerator(
                            customerId,
                            TEMPORARY_ID_ASSET_GROUP
                        ),
                        client.Config
                    );

                List<MutateOperation> conversionGoalOperations =
                    CreateCustomerConversionGoalOperations(
                        customerId,
                        customerConversionGoals
                    );

                // Retail Performance Max campaigns require listing groups, which are created via the
                // AssetGroupListingGroupFilter resource.
                List<MutateOperation> assetGroupListingGroupOperations =
                    CreateAssetGroupListingGroupOperations(
                        assetGroupResourceName
                    );

                MutateGoogleAdsRequest request = new MutateGoogleAdsRequest
                {
                    CustomerId = customerId.ToString()
                };

                // It's important to create these entities in this order because they depend on
                // each other.
                //
                // Additionally, we take several lists of operations and flatten them into one
                // large list.
                request.MutateOperations.Add(campaignBudgetOperation);
                request.MutateOperations.Add(performanceMaxCampaignOperation);
                request.MutateOperations.AddRange(campaignCriterionOperations);
                request.MutateOperations.AddRange(assetGroupOperations);
                request.MutateOperations.AddRange(conversionGoalOperations);
                request.MutateOperations.AddRange(assetGroupListingGroupOperations);

                MutateGoogleAdsResponse response = googleAdsServiceClient.Mutate(request);

                PrintResponseDetails(response);
            }
            catch (GoogleAdsException e)
            {
                Console.WriteLine("Failure:");
                Console.WriteLine($"Message: {e.Message}");
                Console.WriteLine($"Failure: {e.Failure}");
                Console.WriteLine($"Request ID: {e.RequestId}");
                throw;
            }
        }

        /// <summary>
        /// Creates a MutateOperation that creates a new CampaignBudget.
        ///
        /// A temporary ID will be assigned to this campaign budget so that it can be
        /// referenced by other objects being created in the same Mutate request.
        /// </summary>
        /// <param name="budgetResourceName">The temporary resource name of the budget to
        /// create.</param>
        /// <returns>A MutateOperation that creates a CampaignBudget.</returns>
        private MutateOperation CreateCampaignBudgetOperation(
            string budgetResourceName)
        {
            MutateOperation operation = new MutateOperation
            {
                CampaignBudgetOperation = new CampaignBudgetOperation
                {
                    Create = new CampaignBudget
                    {
                        Name = "Performance Max campaign budget #"
                          + ExampleUtilities.GetRandomString(),

                        // The budget period already defaults to Daily.
                        AmountMicros = 50000000,
                        DeliveryMethod = BudgetDeliveryMethod.Standard,

                        // A Performance Max campaign cannot use a shared campaign budget.
                        ExplicitlyShared = false,

                        // Set a temporary ID in the budget's resource name so it can be referenced
                        // by the campaign in later steps.
                        ResourceName = budgetResourceName
                    }
                }
            };

            return operation;
        }


        /// Creates a MutateOperation that creates a new Performance Max campaign.
        /// <param name="campaignResourceName">The campaign resource name.</param>
        /// <param name="campaignBudgetResourceName">The campaign budget resource name.</param>
        /// <param name="merchantCenterAccountId">The Merchant Center account ID.</param>
        /// <returns>A MutateOperations that will create this new campaign.</returns>
        private MutateOperation CreatePerformanceMaxCampaignOperation(
            string campaignResourceName,
            string campaignBudgetResourceName,
            long merchantCenterAccountId)
        {
            MutateOperation operation = new MutateOperation()
            {
                CampaignOperation = new CampaignOperation()
                {
                    Create = new Campaign()
                    {
                        Name = "Performance Max campaign #" + ExampleUtilities.GetRandomString(),

                        // Set the campaign status as PAUSED. The campaign is the only entity in
                        // the mutate request that should have its status set.
                        Status = CampaignStatus.Paused,

                        // All Performance Max campaigns have an advertising_channel_type of
                        // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.
                        AdvertisingChannelType = AdvertisingChannelType.PerformanceMax,

                        // Bidding strategy must be set directly on the campaign. Setting a
                        // portfolio bidding strategy by resource name is not supported. Max
                        // Conversion and Maximize Conversion Value are the only strategies
                        // supported for Performance Max campaigns. BiddingStrategyTYpe is
                        // read-only and cannot be set by the API. An optional ROAS (Return on
                        // Advertising Spend) can be set to enable the MaximizeConversionValue
                        // bidding strategy. The ROAS value must be specified as a ratio in the API.
                        // It is calculated by dividing "total value" by "total spend".
                        //
                        // For more information on Maximize Conversion Value, see the support
                        // article:
                        // http://support.google.com/google-ads/answer/7684216.
                        //
                        // A target_roas of 3.5 corresponds to a 350% return on ad spend.
                        MaximizeConversionValue = new MaximizeConversionValue()
                        {
                            TargetRoas = 3.5
                        },

                        ShoppingSetting = new ShoppingSetting()
                        {
                            MerchantId = merchantCenterAccountId,
                            // Optional: To use products only from a specific feed, set FeedLabel
                            // to the feed label used in Merchant Center.
                            // See: https://support.google.com/merchants/answer/12453549.
                            // Omitting the FeedLabel field will use products from all feeds.
                            // FeedLabel = "INSERT_FEED_LABEL_HERE"
                        },

                        // Set the Final URL expansion opt out. This flag is specific to
                        // Performance Max campaigns. If opted out (True), only the final URLs in
                        // the asset group or URLs specified in the advertiser's Google Merchant
                        // Center or business data feeds are targeted.
                        //
                        // If opted in (False), the entire domain will be targeted. For best
                        // results, set this value to false to opt in and allow URL expansions. You
                        // can optionally add exclusions to limit traffic to parts of your website.
                        //
                        // For a Retail campaign, we want the final URL's to be limited to those
                        // explicitly surfaced via GMC.
                        UrlExpansionOptOut = true,

                        // Use the temporary resource name created earlier
                        ResourceName = campaignResourceName,

                        // Set the budget using the given budget resource name.
                        CampaignBudget = campaignBudgetResourceName,

                        // Optional fields
                        StartDate = DateTime.Now.AddDays(1).ToString("yyyyMMdd"),
                        EndDate = DateTime.Now.AddDays(365).ToString("yyyyMMdd")
                    }
                }
            };

            return operation;
        }


        /// <summary>
        /// Creates a list of MutateOperations that create new campaign criteria.
        /// </summary>
        /// <param name="campaignResourceName">The campaign resource name.</param>
        /// <returns>A list of MutateOperations that create new campaign criteria.</returns>
        private List<MutateOperation> CreateCampaignCriterionOperations(
            string campaignResourceName)
        {
            List<MutateOperation> operations = new List<MutateOperation>();

            // Set the LOCATION campaign criteria.
            // Target all of New York City except Brooklyn.
            // Location IDs are listed here:
            // https://developers.google.com/google-ads/api/reference/data/geotargets
            // and they can also be retrieved using the GeoTargetConstantService as shown
            // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting
            //
            // We will add one positive location target for New York City (ID=1023191)
            // and one negative location target for Brooklyn (ID=1022762).
            // First, add the positive (negative = False) for New York City.
            MutateOperation operation1 = new MutateOperation()
            {
                CampaignCriterionOperation = new CampaignCriterionOperation()
                {
                    Create = new CampaignCriterion()
                    {
                        Campaign = campaignResourceName,
                        Location = new LocationInfo()
                        {
                            GeoTargetConstant = ResourceNames.GeoTargetConstant(1023191)
                        },

                        Negative = false
                    }
                }
            };

            operations.Add(operation1);

            // Next add the negative target for Brooklyn.
            MutateOperation operation2 = new MutateOperation()
            {
                CampaignCriterionOperation = new CampaignCriterionOperation()
                {
                    Create = new CampaignCriterion()
                    {
                        Campaign = campaignResourceName,
                        Location = new LocationInfo()
                        {
                            GeoTargetConstant = ResourceNames.GeoTargetConstant(1022762)
                        },

                        Negative = true
                    }
                }
            };

            operations.Add(operation2);

            // Set the LANGUAGE campaign criterion.
            MutateOperation operation3 = new MutateOperation()
            {
                CampaignCriterionOperation = new CampaignCriterionOperation()
                {
                    Create = new CampaignCriterion()
                    {
                        Campaign = campaignResourceName,

                        // Set the language.
                        // For a list of all language codes, see:
                        // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7
                        Language = new LanguageInfo()
                        {
                            LanguageConstant = ResourceNames.LanguageConstant(1000) // English
                        },
                    }
                }
            };

            operations.Add(operation3);

            return operations;
        }


        /// <summary>
        /// Creates multiple text assets and returns the list of resource names.
        /// These repeated assets must be created in a separate request prior to
        /// creating the campaign.
        /// </summary>
        /// <param name="client">The Google Ads Client.</param>
        /// <param name="customerId">The customer's ID.</param>
        /// <param name="texts">The texts to add.</param>
        /// <returns>A list of asset resource names.</returns>
        private List<string> CreateMultipleTextAssets(
            GoogleAdsClient client,
            long customerId,
            string[] texts)
        {
            // Get the GoogleAdsService.
            GoogleAdsServiceClient googleAdsServiceClient =
                client.GetService(Services.V17.GoogleAdsService);

            MutateGoogleAdsRequest request = new MutateGoogleAdsRequest()
            {
                CustomerId = customerId.ToString()
            };

            foreach (string text in texts)
            {
                request.MutateOperations.Add(
                    new MutateOperation()
                    {
                        AssetOperation = new AssetOperation()
                        {
                            Create = new Asset()
                            {
                                TextAsset = new TextAsset()
                                {
                                    Text = text
                                }
                            }
                        }
                    }
                );
            }

            // Send the operations in a single Mutate request.
            MutateGoogleAdsResponse response = googleAdsServiceClient.Mutate(request);

            List<string> assetResourceNames = new List<string>();

            foreach (MutateOperationResponse operationResponse in response.MutateOperationResponses)
            {
                MutateAssetResult assetResult = operationResponse.AssetResult;
                assetResourceNames.Add(assetResult.ResourceName);
            }

            PrintResponseDetails(response);

            return assetResourceNames;
        }


        /// <summary>
        /// Creates a list of MutateOperations that create a new asset_group.
        /// </summary>
        /// <param name="campaignResourceName">The campaign resource name.</param>
        /// <param name="assetGroupResourceName">The asset group resource name.</param>
        /// <param name="finalUrl">The final url.</param>
        /// <param name="headlineAssetResourceNames">The headline asset resource names.</param>
        /// <param name="descriptionAssetResourceNames">The description asset resource
        /// names.</param>
        /// <param name="resourceNameGenerator">A generator for unique temporary ID's.</param>
        /// <param name="config">The Google Ads config.</param>
        /// <returns>A list of MutateOperations that create the new asset group.</returns>
        private List<MutateOperation> CreateAssetGroupOperations(
            string campaignResourceName,
            string assetGroupResourceName,
            string finalUrl,
            List<string> headlineAssetResourceNames,
            List<string> descriptionAssetResourceNames,
            AssetGroupAssetTemporaryResourceNameGenerator resourceNameGenerator,
            GoogleAdsConfig config)
        {
            List<MutateOperation> operations = new List<MutateOperation>();

            // For the list of required assets for a Performance Max campaign, see
            // https://developers.google.com/google-ads/api/docs/performance-max/assets

            // Create and link the long headline text asset.
            string longHeadlineResourceName = resourceNameGenerator.Next();
            operations.Add(
                CreateTextAssetOperation(
                    longHeadlineResourceName,
                    "Travel the World"
                )
            );

            // Create the business name text asset.
            string businessNameResourceName = resourceNameGenerator.Next();
            operations.Add(
                CreateTextAssetOperation(
                    businessNameResourceName,
                    "Interplanetary Cruises"
                )
            );

            // Create the Logo Asset.
            string logoResourceName = resourceNameGenerator.Next();
            operations.Add(
                CreateImageAssetOperation(
                    logoResourceName,
                    "https://gaagl.page.link/1Crm",
                    "Logo Image",
                    config
                )
            );

            // Create the Marketing Image Asset.
            string marketingImageResourceName = resourceNameGenerator.Next();
            operations.Add(
                CreateImageAssetOperation(
                    marketingImageResourceName,
                    "https://gaagl.page.link/Eit5",
                    "Marketing Image",
                    config
                )
            );

            // Create the Square Marketing Image Asset.
            string squareMarketingImageResourceName = resourceNameGenerator.Next();
            operations.Add(
                CreateImageAssetOperation(
                    squareMarketingImageResourceName,
                    "https://gaagl.page.link/bjYi",
                    "Square Marketing Image",
                    config
                )
            );

            // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset
            // and providing:
            //   the resource name of the AssetGroup
            //   the resource name of the Asset
            //   the field_type of the Asset in this AssetGroup.
            //
            // To learn more about AssetGroups, see
            // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups
            //
            // Also, note that all asset creation operations must be before the
            // asset group creation operation and the asset group linking operations.

            // Create the AssetGroup
            operations.Add(
                new MutateOperation()
                {
                    AssetGroupOperation = new AssetGroupOperation()
                    {
                        Create = new AssetGroup()
                        {
                            Name = "Performance Max asset group #" +
                                ExampleUtilities.GetRandomString(),

                            Campaign = campaignResourceName,
                            FinalUrls = { finalUrl },
                            FinalMobileUrls = { finalUrl },
                            Status = AssetGroupStatus.Paused,
                            ResourceName = assetGroupResourceName
                        }
                    }
                }
            );

            // Link the previously created assets.

            // Link the headline assets.
            foreach (string resourceName in headlineAssetResourceNames)
            {
                operations.Add(
                    new MutateOperation()
                    {
                        AssetGroupAssetOperation = new AssetGroupAssetOperation()
                        {
                            Create = new AssetGroupAsset()
                            {
                                FieldType = AssetFieldType.Headline,
                                AssetGroup = assetGroupResourceName,
                                Asset = resourceName
                            }
                        }
                    }
                );
            }

            // Link the description assets.
            foreach (string resourceName in descriptionAssetResourceNames)
            {
                operations.Add(
                    new MutateOperation()
                    {
                        AssetGroupAssetOperation = new AssetGroupAssetOperation()
                        {
                            Create = new AssetGroupAsset()
                            {
                                FieldType = AssetFieldType.Description,
                                AssetGroup = assetGroupResourceName,
                                Asset = resourceName
                            }
                        }
                    }
                );
            }

            operations.Add(
                CreateLinkAssetOperation(
                    AssetFieldType.LongHeadline,
                    assetGroupResourceName,
                    longHeadlineResourceName
                )
            );

            operations.Add(
                CreateLinkAssetOperation(
                    AssetFieldType.BusinessName,
                    assetGroupResourceName,
                    businessNameResourceName
                )
            );

            operations.Add(
                CreateLinkAssetOperation(
                    AssetFieldType.Logo,
                    assetGroupResourceName,
                    logoResourceName
                )
            );

            operations.Add(
                CreateLinkAssetOperation(
                    AssetFieldType.MarketingImage,
                    assetGroupResourceName,
                    marketingImageResourceName
                )
            );

            operations.Add(
                CreateLinkAssetOperation(
                    AssetFieldType.SquareMarketingImage,
                    assetGroupResourceName,
                    squareMarketingImageResourceName
                )
            );

            return operations;
        }


        /// <summary>
        /// Creates a MutateOperation that creates a new text asset.
        /// </summary>
        /// <param name="assetResourceName">The resource name of the text asset to be
        /// created.</param>
        /// <param name="text">The text of the asset to be created.</param>
        /// <returns>A MutateOperation that creates the new text asset.</returns>
        private MutateOperation CreateTextAssetOperation(
            string assetResourceName,
            string text) => new MutateOperation()
            {
                AssetOperation = new AssetOperation()
                {
                    Create = new Asset()
                    {
                        ResourceName = assetResourceName,
                        TextAsset = new TextAsset()
                        {
                            Text = text
                        }
                    }
                }
            };


        /// <summary>
        /// Creates a MutateOperation that creates a new image asset.
        /// </summary>
        /// <param name="assetResourceName">The resource name of the text asset to be
        /// created.</param>
        /// <param name="url">The url of the image to be retrieved and put into an asset.</param>
        /// <param name="assetName">The asset name.</param>
        /// <param name="config">The Google Ads config.</param>
        /// <returns>A MutateOperation that creates a new image asset.</returns>
        private MutateOperation CreateImageAssetOperation(
            string assetResourceName,
            string url,
            string assetName,
            GoogleAdsConfig config) => new MutateOperation()
            {
                AssetOperation = new AssetOperation()
                {
                    Create = new Asset()
                    {
                        ResourceName = assetResourceName,
                        ImageAsset = new ImageAsset()
                        {
                            Data =
                            ByteString.CopyFrom(
                                MediaUtilities.GetAssetDataFromUrl(url, config)
                            )
                        },
                        // Provide a unique friendly name to identify your asset.
                        // When there is an existing image asset with the same content but a
                        // different name, the new name will be dropped silently.
                        Name = assetName
                    }
                }
            };


        /// <summary>
        /// Creates a MutateOperation that links an asset to an asset group.
        /// </summary>
        /// <param name="fieldType">The field type of the asset to be linked.</param>
        /// <param name="assetGroupResourceName">The resource name of the asset group
        /// to link the asset to.</param>
        /// <param name="assetResourceName">The resource name of the text asset to be
        /// linked.</param>
        /// <returns>A MutateOperation that links an asset to an asset group.</returns>
        private MutateOperation CreateLinkAssetOperation(
            AssetFieldType fieldType,
            string assetGroupResourceName,
            string assetResourceName) => new MutateOperation()
            {
                AssetGroupAssetOperation = new AssetGroupAssetOperation()
                {
                    Create = new AssetGroupAsset()
                    {
                        FieldType = fieldType,
                        AssetGroup = assetGroupResourceName,
                        Asset = assetResourceName
                    }
                }
            };


        /// <summary>
        /// Retrieves the list of customer conversion goals.
        /// </summary>
        /// <param name="client">The Google Ads Client.</param>
        /// <param name="customerId">The customer's id.</param>
        /// <returns>A list customer conversion goals.</returns>
        private List<CustomerConversionGoal> GetCustomerConversionGoals(
            GoogleAdsClient client,
            long customerId)
        {
            // Get the GoogleAdsService.
            GoogleAdsServiceClient googleAdsServiceClient =
                client.GetService(Services.V17.GoogleAdsService);

            List<CustomerConversionGoal> conversionGoals = new List<CustomerConversionGoal>();

            SearchGoogleAdsRequest request = new SearchGoogleAdsRequest()
            {
                CustomerId = customerId.ToString(),
                Query =
                    @"SELECT
                        customer_conversion_goal.category,
                        customer_conversion_goal.origin
                    FROM
                        customer_conversion_goal"
            };

            // The number of conversion goals is typically less than 50 so we use
            // GoogleAdsService.search instead of search_stream.
            PagedEnumerable<SearchGoogleAdsResponse, GoogleAdsRow> searchPagedResponse =
                googleAdsServiceClient.Search(request);

            // Iterate over the results and build the list of conversion goals.
            foreach (GoogleAdsRow row in searchPagedResponse)
            {
                conversionGoals.Add(row.CustomerConversionGoal);
            }

            return conversionGoals;
        }

        /// <summary>
        /// Creates a list of MutateOperations that override customer conversion goals.
        /// </summary>
        /// <param name="customerId">The customer's id.</param>
        /// <param name="conversionGoals">A list customer conversion goals.</param>
        /// <returns>A list customer conversion goal operations.</returns>
        private List<MutateOperation> CreateCustomerConversionGoalOperations(
            long customerId,
            List<CustomerConversionGoal> conversionGoals)
        {
            List<MutateOperation> operations =
                new List<MutateOperation>();

            foreach (CustomerConversionGoal conversionGoal in conversionGoals)
            {
                CustomerConversionGoal newConversionGoal = new CustomerConversionGoal()
                {
                    ResourceName = ResourceNames.CustomerConversionGoal(
                        customerId,
                        conversionGoal.Category,
                        conversionGoal.Origin
                    ),
                };

                // Change the biddability for the campaign conversion goal.
                // Set biddability to True for the desired (category, origin).
                // Set biddability to False for all other conversion goals.
                // Note:
                //  1- It is assumed that this Conversion Action
                //     (category=PURCHASE, origin=WEBSITE) exists in this account.
                //  2- More than one goal can be biddable if desired. This example
                //     shows only one.
                newConversionGoal.Biddable =
                    conversionGoal.Category == ConversionActionCategory.Purchase &&
                    conversionGoal.Origin == ConversionOrigin.Website;

                operations.Add(
                    new MutateOperation()
                    {
                        CustomerConversionGoalOperation = new CustomerConversionGoalOperation()
                        {
                            Update = newConversionGoal,
                            UpdateMask = FieldMasks.AllSetFieldsOf(newConversionGoal)
                        }
                    }
                );
            }

            return operations;
        }


        /// <summary>
        /// Creates a list of MutateOperations that create a new asset group
        /// listing group filter.
        /// </summary>
        /// <param name="assetGroupResourceName">The resource name of the asset group.</param>
        /// <returns>A list of mutate operations.</returns>
        private List<MutateOperation> CreateAssetGroupListingGroupOperations(
            string assetGroupResourceName)
        {
            List<MutateOperation> operations = new List<MutateOperation>();

            // Creates a new ad group criterion containing the "default" listing group (All
            // products).
            AssetGroupListingGroupFilter listingGroupFilter = new AssetGroupListingGroupFilter()
            {
                AssetGroup = assetGroupResourceName,

                // Since this is the root node, do not set the ParentListingGroupFilter. For all
                // other nodes, this would refer to the parent listing group filter resource name.
                // ParentListingGroupFilter = "<PARENT FILTER NAME>"

                // The UnitIncluded means this node has no children.
                Type = ListingGroupFilterType.UnitIncluded,

                // Because this is a Performance Max campaign for retail, we need to specify that
                // this is in the shopping listing source.
                ListingSource = ListingGroupFilterListingSource.Shopping
            };

            AssetGroupListingGroupFilterOperation operation =
                new AssetGroupListingGroupFilterOperation()
                {
                    Create = listingGroupFilter
                };

            operations.Add(
                new MutateOperation()
                {
                    AssetGroupListingGroupFilterOperation = operation
                }
            );

            return operations;
        }


        /// <summary>
        /// Prints the details of a MutateGoogleAdsResponse. Parses the "response" oneof field name
        /// and uses it to extract the new entity's name and resource name.
        /// </summary>
        /// <param name="response">A MutateGoogleAdsResponse instance.</param>
        private void PrintResponseDetails(MutateGoogleAdsResponse response)
        {
            // Parse the Mutate response to print details about the entities that were created
            // in the request.
            foreach (MutateOperationResponse operationResponse in response.MutateOperationResponses)
            {
                string resourceName;

                string entityName = operationResponse.ResponseCase.ToString();
                // Trim the substring "Result" from the end of the entity name.
                entityName = entityName.Remove(entityName.Length - 6);

                switch (operationResponse.ResponseCase)
                {
                    case MutateOperationResponse.ResponseOneofCase.AdGroupResult:
                        resourceName = operationResponse.AdGroupResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.AdGroupAdResult:
                        resourceName = operationResponse.AdGroupAdResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.CampaignResult:
                        resourceName = operationResponse.CampaignResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.CampaignBudgetResult:
                        resourceName = operationResponse.CampaignBudgetResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.CampaignCriterionResult:
                        resourceName = operationResponse.CampaignCriterionResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.SmartCampaignSettingResult:
                        resourceName = operationResponse.SmartCampaignSettingResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.AssetResult:
                        resourceName = operationResponse.AssetResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.AssetGroupAssetResult:
                        resourceName = operationResponse.AssetGroupAssetResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.AssetGroupResult:
                        resourceName = operationResponse.AssetGroupResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.AssetGroupListingGroupFilterResult:
                        resourceName = operationResponse.AssetGroupListingGroupFilterResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.CampaignConversionGoalResult:
                        resourceName = operationResponse.CampaignConversionGoalResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.CustomerConversionGoalResult:
                        resourceName = operationResponse.CustomerConversionGoalResult.ResourceName;
                        break;

                    default:
                        resourceName = "<not found>";
                        break;
                }

                Console.WriteLine(
                    $"Created a(n) {entityName} with resource name: '{resourceName}'.");
            }
        }
    }
}

      

PHP

<?php

/**
 * Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace Google\Ads\GoogleAds\Examples\ShoppingAds;

require __DIR__ . '/../../vendor/autoload.php';

use GetOpt\GetOpt;
use Google\Ads\GoogleAds\Examples\Utils\ArgumentNames;
use Google\Ads\GoogleAds\Examples\Utils\ArgumentParser;
use Google\Ads\GoogleAds\Examples\Utils\Helper;
use Google\Ads\GoogleAds\Lib\OAuth2TokenBuilder;
use Google\Ads\GoogleAds\Lib\V17\GoogleAdsClient;
use Google\Ads\GoogleAds\Lib\V17\GoogleAdsClientBuilder;
use Google\Ads\GoogleAds\Lib\V17\GoogleAdsException;
use Google\Ads\GoogleAds\Util\FieldMasks;
use Google\Ads\GoogleAds\Util\V17\ResourceNames;
use Google\Ads\GoogleAds\V17\Common\ImageAsset;
use Google\Ads\GoogleAds\V17\Common\LanguageInfo;
use Google\Ads\GoogleAds\V17\Common\LocationInfo;
use Google\Ads\GoogleAds\V17\Common\MaximizeConversionValue;
use Google\Ads\GoogleAds\V17\Common\TextAsset;
use Google\Ads\GoogleAds\V17\Enums\AdvertisingChannelTypeEnum\AdvertisingChannelType;
use Google\Ads\GoogleAds\V17\Enums\AssetFieldTypeEnum\AssetFieldType;
use Google\Ads\GoogleAds\V17\Enums\AssetGroupStatusEnum\AssetGroupStatus;
use Google\Ads\GoogleAds\V17\Enums\BudgetDeliveryMethodEnum\BudgetDeliveryMethod;
use Google\Ads\GoogleAds\V17\Enums\CampaignStatusEnum\CampaignStatus;
use Google\Ads\GoogleAds\V17\Enums\ConversionActionCategoryEnum\ConversionActionCategory;
use Google\Ads\GoogleAds\V17\Enums\ConversionOriginEnum\ConversionOrigin;
use Google\Ads\GoogleAds\V17\Enums\ListingGroupFilterListingSourceEnum\ListingGroupFilterListingSource;
use Google\Ads\GoogleAds\V17\Enums\ListingGroupFilterTypeEnum\ListingGroupFilterType;
use Google\Ads\GoogleAds\V17\Errors\GoogleAdsError;
use Google\Ads\GoogleAds\V17\Resources\Asset;
use Google\Ads\GoogleAds\V17\Resources\AssetGroup;
use Google\Ads\GoogleAds\V17\Resources\AssetGroupAsset;
use Google\Ads\GoogleAds\V17\Resources\AssetGroupListingGroupFilter;
use Google\Ads\GoogleAds\V17\Resources\Campaign;
use Google\Ads\GoogleAds\V17\Resources\Campaign\ShoppingSetting;
use Google\Ads\GoogleAds\V17\Resources\CampaignBudget;
use Google\Ads\GoogleAds\V17\Resources\CampaignConversionGoal;
use Google\Ads\GoogleAds\V17\Resources\CampaignCriterion;
use Google\Ads\GoogleAds\V17\Services\AssetGroupAssetOperation;
use Google\Ads\GoogleAds\V17\Services\AssetGroupListingGroupFilterOperation;
use Google\Ads\GoogleAds\V17\Services\AssetGroupOperation;
use Google\Ads\GoogleAds\V17\Services\AssetOperation;
use Google\Ads\GoogleAds\V17\Services\CampaignBudgetOperation;
use Google\Ads\GoogleAds\V17\Services\CampaignConversionGoalOperation;
use Google\Ads\GoogleAds\V17\Services\CampaignCriterionOperation;
use Google\Ads\GoogleAds\V17\Services\CampaignOperation;
use Google\Ads\GoogleAds\V17\Services\GoogleAdsRow;
use Google\Ads\GoogleAds\V17\Services\MutateGoogleAdsRequest;
use Google\Ads\GoogleAds\V17\Services\MutateGoogleAdsResponse;
use Google\Ads\GoogleAds\V17\Services\MutateOperation;
use Google\Ads\GoogleAds\V17\Services\MutateOperationResponse;
use Google\Ads\GoogleAds\V17\Services\SearchGoogleAdsRequest;
use Google\ApiCore\ApiException;
use Google\ApiCore\Serializer;

/**
 * This example shows how to create a Performance Max retail campaign.
 *
 * This will be created for "All products".
 *
 * For more information about Performance Max retail campaigns, see
 * https://developers.google.com/google-ads/api/docs/performance-max/retail.
 *
 * Prerequisites:
 * - You need to have access to a Merchant Center account. You can find
 * instructions to create a Merchant Center account here:
 * https://support.google.com/merchants/answer/188924.
 * This account must be linked to your Google Ads account. The integration
 * instructions can be found at:
 * https://developers.google.com/google-ads/api/docs/shopping-ads/merchant-center
 * - You need your Google Ads account to track conversions. The different ways
 * to track conversions can be found here:
 * https://support.google.com/google-ads/answer/1722054.
 * - You must have at least one conversion action in the account. For more about conversion
 * actions, see
 * https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions.
 */
class AddPerformanceMaxRetailCampaign
{
    private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';
    private const MERCHANT_CENTER_ACCOUNT_ID = 'INSERT_MERCHANT_CENTER_ACCOUNT_ID_HERE';
    private const SALES_COUNTRY = 'US';
    // The final URL for the generated ads. Must have the same domain as the Merchant Center
    // account.
    private const FINAL_URL = 'INSERT_FINAL_URL_HERE';

    // We specify temporary IDs that are specific to a single mutate request.
    // Temporary IDs are always negative and unique within one mutate request.
    //
    // See https://developers.google.com/google-ads/api/docs/mutating/best-practices
    // for further details.
    //
    // These temporary IDs are fixed because they are used in multiple places.
    private const BUDGET_TEMPORARY_ID = -1;
    private const PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = -2;
    private const ASSET_GROUP_TEMPORARY_ID = -3;

    // There are also entities that will be created in the same request but do not need to be fixed
    // temporary IDs because they are referenced only once.
    /** @var int the negative temporary ID used in bulk mutates. */
    private static $nextTempId = self::ASSET_GROUP_TEMPORARY_ID - 1;

    public static function main()
    {
        // Either pass the required parameters for this example on the command line, or insert them
        // into the constants above.
        $options = (new ArgumentParser())->parseCommandArguments([
            ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,
            ArgumentNames::MERCHANT_CENTER_ACCOUNT_ID => GetOpt::REQUIRED_ARGUMENT,
            ArgumentNames::FINAL_URL => GetOpt::REQUIRED_ARGUMENT
        ]);

        // Generate a refreshable OAuth2 credential for authentication.
        $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();

        // Construct a Google Ads client configured from a properties file and the
        // OAuth2 credentials above.
        $googleAdsClient = (new GoogleAdsClientBuilder())
            ->fromFile()
            ->withOAuth2Credential($oAuth2Credential)
            // We set this value to true to show how to use GAPIC v2 source code. You can remove the
            // below line if you wish to use the old-style source code. Note that in that case, you
            // probably need to modify some parts of the code below to make it work.
            // For more information, see
            // https://developers.devsite.corp.google.com/google-ads/api/docs/client-libs/php/gapic.
            ->usingGapicV2Source(true)
            ->build();

        try {
            self::runExample(
                $googleAdsClient,
                $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,
                $options[ArgumentNames::MERCHANT_CENTER_ACCOUNT_ID]
                    ?: self::MERCHANT_CENTER_ACCOUNT_ID,
                $options[ArgumentNames::SALES_COUNTRY] ?: self::SALES_COUNTRY,
                $options[ArgumentNames::FINAL_URL] ?: self::FINAL_URL
            );
        } catch (GoogleAdsException $googleAdsException) {
            printf(
                "Request with ID '%s' has failed.%sGoogle Ads failure details:%s",
                $googleAdsException->getRequestId(),
                PHP_EOL,
                PHP_EOL
            );
            foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {
                /** @var GoogleAdsError $error */
                printf(
                    "\t%s: %s%s",
                    $error->getErrorCode()->getErrorCode(),
                    $error->getMessage(),
                    PHP_EOL
                );
            }
            exit(1);
        } catch (ApiException $apiException) {
            printf(
                "ApiException was thrown with message '%s'.%s",
                $apiException->getMessage(),
                PHP_EOL
            );
            exit(1);
        }
    }

    /**
     * Runs the example.
     *
     * @param GoogleAdsClient $googleAdsClient the Google Ads API client
     * @param int $customerId the customer ID
     * @param int $merchantCenterAccountId the Merchant Center account ID
     * @param string $finalUrl the final URL for the asset group of the campaign
     */
    public static function runExample(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        int $merchantCenterAccountId,
        string $finalUrl
    ) {
        // This campaign will override the customer conversion goals.
        // Retrieves the current list of customer conversion goals.
        $customerConversionGoals = self::getCustomerConversionGoals(
            $googleAdsClient,
            $customerId
        );

        // Performance Max campaigns require that repeated assets such as headlines
        // and descriptions be created before the campaign.
        // For the list of required assets for a Performance Max campaign, see
        // https://developers.google.com/google-ads/api/docs/performance-max/assets.
        //
        // Creates the headlines.
        $headlineAssetResourceNames = self::createMultipleTextAssets(
            $googleAdsClient,
            $customerId,
            ["Travel", "Travel Reviews", "Book travel"]
        );
        // Creates the descriptions.
        $descriptionAssetResourceNames = self::createMultipleTextAssets(
            $googleAdsClient,
            $customerId,
            ["Take to the air!", "Fly to the sky!"]
        );

        // It's important to create the below entities in this order because they depend on
        // each other.
        $operations = [];
        // The below methods create and return MutateOperations that we later
        // provide to the GoogleAdsService.Mutate method in order to create the
        // entities in a single request. Since the entities for a Performance Max
        // campaign are closely tied to one-another, it's considered a best practice
        // to create them in a single Mutate request so they all complete
        // successfully or fail entirely, leaving no orphaned entities. See:
        // https://developers.google.com/google-ads/api/docs/mutating/overview.
        $operations[] = self::createCampaignBudgetOperation($customerId);
        $operations[] = self::createPerformanceMaxCampaignOperation(
            $customerId,
            $merchantCenterAccountId
        );
        $operations =
            array_merge($operations, self::createCampaignCriterionOperations($customerId));
        $operations[] = self::createAssetGroupOperation($customerId, $finalUrl);
        $operations[] = self::createAssetGroupListingGroupFilterOperation($customerId);
        $operations = array_merge($operations, self::createAssetandAssetGroupAssetOperations(
            $customerId,
            $headlineAssetResourceNames,
            $descriptionAssetResourceNames
        ));
        $operations = array_merge($operations, self::createConversionGoalOperations(
            $customerId,
            $customerConversionGoals
        ));

        // Issues a mutate request to create everything and prints its information.
        $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
        $response = $googleAdsServiceClient->mutate(
            MutateGoogleAdsRequest::build($customerId, $operations)
        );

        self::printResponseDetails($response);
    }

    /**
     * Creates a MutateOperation that creates a new CampaignBudget.
     *
     * A temporary ID will be assigned to this campaign budget so that it can be
     * referenced by other objects being created in the same Mutate request.
     *
     * @param int $customerId the customer ID
     * @return MutateOperation the mutate operation that creates a campaign budget
     */
    private static function createCampaignBudgetOperation(int $customerId): MutateOperation
    {
        // Creates a mutate operation that creates a campaign budget operation.
        return new MutateOperation([
            'campaign_budget_operation' => new CampaignBudgetOperation([
                'create' => new CampaignBudget([
                    // Sets a temporary ID in the budget's resource name so it can be referenced
                    // by the campaign in later steps.
                    'resource_name' => ResourceNames::forCampaignBudget(
                        $customerId,
                        self::BUDGET_TEMPORARY_ID
                    ),
                    'name' => 'Performance Max retail campaign budget #' .
                        Helper::getPrintableDatetime(),
                    // The budget period already defaults to DAILY.
                    'amount_micros' => 50000000,
                    'delivery_method' => BudgetDeliveryMethod::STANDARD,
                    // A Performance Max campaign cannot use a shared campaign budget.
                    'explicitly_shared' => false
                ])
            ])
        ]);
    }

    /**
     * Creates a MutateOperation that creates a new Performance Max campaign.
     *
     * A temporary ID will be assigned to this campaign so that it can
     * be referenced by other objects being created in the same Mutate request.
     *
     * @param int $customerId the customer ID
     * @param int $merchantCenterAccountId the Merchant Center account ID
     * @return MutateOperation the mutate operation that creates the campaign
     */
    private static function createPerformanceMaxCampaignOperation(
        int $customerId,
        int $merchantCenterAccountId
    ): MutateOperation {
        // Creates a mutate operation that creates a campaign operation.
        return new MutateOperation([
            'campaign_operation' => new CampaignOperation([
                'create' => new Campaign([
                    'name' => 'Performance Max retail campaign #' . Helper::getPrintableDatetime(),
                    // Assigns the resource name with a temporary ID.
                    'resource_name' => ResourceNames::forCampaign(
                        $customerId,
                        self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                    ),
                    // Sets the budget using the given budget resource name.
                    'campaign_budget' => ResourceNames::forCampaignBudget(
                        $customerId,
                        self::BUDGET_TEMPORARY_ID
                    ),
                    // The campaign is the only entity in the mutate request that should have its
                    // status set.
                    // Recommendation: Set the campaign to PAUSED when creating it to prevent
                    // the ads from immediately serving.
                    'status' => CampaignStatus::PAUSED,
                    // All Performance Max campaigns have an advertising_channel_type of
                    // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.
                    'advertising_channel_type' => AdvertisingChannelType::PERFORMANCE_MAX,

                    // Bidding strategy must be set directly on the campaign.
                    // Setting a portfolio bidding strategy by resource name is not supported.
                    // Max Conversion and Max Conversion Value are the only strategies supported
                    // for Performance Max campaigns.
                    // An optional ROAS (Return on Advertising Spend) can be set for
                    // maximize_conversion_value. The ROAS value must be specified as a ratio in
                    // the API. It is calculated by dividing "total value" by "total spend".
                    // For more information on Max Conversion Value, see the support article:
                    // http://support.google.com/google-ads/answer/7684216.
                    // A target_roas of 3.5 corresponds to a 350% return on ad spend.
                    'maximize_conversion_value' => new MaximizeConversionValue([
                        'target_roas' => 3.5
                    ]),
                    // Below is what you would use if you want to maximize conversions
                    // You can optionally set the 'target_cpa_micros' field on MaximizeConversions.
                    // This is the average amount that you would like to spend per conversion
                    // action.
                    // 'maximize_conversions' => new MaximizeConversions(),

                    // Sets the Final URL expansion opt out. This flag is specific to
                    // Performance Max campaigns. If opted out (true), only the final URLs in
                    // the asset group or URLs specified in the advertiser's Google Merchant
                    // Center or business data feeds are targeted.
                    //
                    // If opted in (false), the entire domain will be targeted. For best
                    // results, set this value to false to opt in and allow URL expansions. You
                    // can optionally add exclusions to limit traffic to parts of your website.
                    //
                    // For a Retail campaign, we want the final URL's to be limited to those
                    // explicitly surfaced via GMC.
                    'url_expansion_opt_out' => true,

                    // Sets the shopping settings.
                    'shopping_setting' => new ShoppingSetting([
                        'merchant_id' => $merchantCenterAccountId,
                        // Optional: To use products only from a specific feed, set feed_label to
                        // the feed label used in Merchant Center.
                        // See: https://support.google.com/merchants/answer/12453549.
                        // Removing the feed_label field will use products from all feeds.
                        // 'feed_label' => 'INSERT_FEED_LABEL_HERE'
                    ]),

                    // Optional fields.
                    'start_date' => date('Ymd', strtotime('+1 day')),
                    'end_date' => date('Ymd', strtotime('+365 days'))
                ])
            ])
        ]);
    }

    /**
     * Creates a list of MutateOperations that create new campaign criteria.
     *
     * @param int $customerId the customer ID
     * @return MutateOperation[] a list of MutateOperations that create the new campaign criteria
     */
    private static function createCampaignCriterionOperations(int $customerId): array
    {
        $operations = [];
        // Sets the LOCATION campaign criteria.
        // Target all of New York City except Brooklyn.
        // Location IDs are listed here:
        // https://developers.google.com/google-ads/api/reference/data/geotargets
        // and they can also be retrieved using the GeoTargetConstantService as shown
        // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting
        $operations[] = new MutateOperation([
            'campaign_criterion_operation' => new CampaignCriterionOperation([
                'create' => new CampaignCriterion([
                    'campaign' => ResourceNames::forCampaign(
                        $customerId,
                        self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                    ),
                    'location' => new LocationInfo([
                        // Adds one positive location target for New York City (ID=1023191),
                        // specifically adding the positive criteria before the negative one.
                        'geo_target_constant' => ResourceNames::forGeoTargetConstant(1023191)
                    ]),
                    'negative' => false
                ])
            ])
        ]);

        // Next adds the negative target for Brooklyn.
        $operations[] = new MutateOperation([
            'campaign_criterion_operation' => new CampaignCriterionOperation([
                'create' => new CampaignCriterion([
                    'campaign' => ResourceNames::forCampaign(
                        $customerId,
                        self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                    ),
                    'location' => new LocationInfo([
                        // Next add the negative target for Brooklyn (ID=1022762).
                        'geo_target_constant' => ResourceNames::forGeoTargetConstant(1022762)
                    ]),
                    'negative' => true
                ])
            ])
        ]);

        // Sets the LANGUAGE campaign criterion.
        $operations[] = new MutateOperation([
            'campaign_criterion_operation' => new CampaignCriterionOperation([
                'create' => new CampaignCriterion([
                    'campaign' => ResourceNames::forCampaign(
                        $customerId,
                        self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                    ),
                    // Sets the language.
                    // For a list of all language codes, see:
                    // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7
                    'language' => new LanguageInfo([
                        'language_constant' => ResourceNames::forLanguageConstant(1000)  // English
                    ])
                ])
            ])
        ]);

        return $operations;
    }

    /**
     * Creates multiple text assets and returns the list of resource names.
     *
     * These repeated assets must be created in a separate request prior to creating the campaign.
     *
     * @param GoogleAdsClient $googleAdsClient the Google Ads API client
     * @param int $customerId the customer ID
     * @param string[] $texts a list of strings, each of which will be used to create a text asset
     * @return string[] a list of asset resource names
     */
    private static function createMultipleTextAssets(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        array $texts
    ): array {
        // Here again, we use the GoogleAdService to create multiple text assets in a single
        // request.
        $operations = [];
        foreach ($texts as $text) {
            // Creates a mutate operation for a text asset.
            $operations[] = new MutateOperation([
                'asset_operation' => new AssetOperation([
                    'create' => new Asset(['text_asset' => new TextAsset(['text' => $text])])
                ])
            ]);
        }

        // Issues a mutate request to add all assets.
        $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
        /** @var MutateGoogleAdsResponse $mutateGoogleAdsResponse */
        $mutateGoogleAdsResponse = $googleAdsServiceClient->mutate(
            MutateGoogleAdsRequest::build($customerId, $operations)
        );

        $assetResourceNames = [];
        foreach ($mutateGoogleAdsResponse->getMutateOperationResponses() as $response) {
            /** @var MutateOperationResponse $response */
            $assetResourceNames[] = $response->getAssetResult()->getResourceName();
        }
        self::printResponseDetails($mutateGoogleAdsResponse);

        return $assetResourceNames;
    }

    /**
     * Creates a MutateOperation that creates a new asset group.
     *
     * A temporary ID will be assigned to this asset group so that it can
     * be referenced by other objects being created in the same Mutate request.
     *
     * @param int $customerId the customer ID
     * @return MutateOperation a mutate operation creates a new asset group.
     */
    private static function createAssetGroupOperation(
        int $customerId,
        string $finalUrl
    ): MutateOperation {
        // Creates a new mutate operation that creates an asset group operation.
        return new MutateOperation([
            'asset_group_operation' => new AssetGroupOperation([
                'create' => new AssetGroup([
                    'resource_name' => ResourceNames::forAssetGroup(
                        $customerId,
                        self::ASSET_GROUP_TEMPORARY_ID
                    ),
                    'name' => 'Performance Max retail asset group #' .
                        Helper::getPrintableDatetime(),
                    'campaign' => ResourceNames::forCampaign(
                        $customerId,
                        self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                    ),
                    'final_urls' => [$finalUrl],
                    'final_mobile_urls' => [$finalUrl],
                    'status' => AssetGroupStatus::PAUSED
                ])
            ])
        ]);
    }

    /**
     * Creates a MutateOperation that creates a new asset group listing group filter.
     *
     * A temporary ID will be assigned to this listing group filter so that it can be referenced by
     * other objects being created in the same Mutate request.
     *
     * @param int $customerId the customer ID
     * @return MutateOperation a MutateOperation that creates a new asset group listing group filter
     */
    private static function createAssetGroupListingGroupFilterOperation(
        int $customerId
    ): MutateOperation {
        return new MutateOperation([
            'asset_group_listing_group_filter_operation'
            => new AssetGroupListingGroupFilterOperation([
                // Creates a new asset group listing group filter containing the "default"
                // listing group (All products).
                'create' => new AssetGroupListingGroupFilter([
                    'asset_group' => ResourceNames::forAssetGroup(
                        $customerId,
                        self::ASSET_GROUP_TEMPORARY_ID
                    ),
                    // Since this is the root node, do not set the 'parent_listing_group_filter'
                    // field. For all other nodes, this would refer to the parent listing group
                    // filter resource name.
                    //
                    // UNIT_INCLUDED means this node has no children.
                    'type' => ListingGroupFilterType::UNIT_INCLUDED,
                    // Because this is a Performance Max campaign for retail, we need to specify
                    // that this is in the shopping listing source.
                    'listing_source' => ListingGroupFilterListingSource::SHOPPING
                ])
            ])
        ]);
    }

    /**
     * Creates a list of MutateOperations that create new asset group asset and assets.
     *
     * A temporary ID will be assigned to this asset group so that it can
     * be referenced by other objects being created in the same mutate request.
     *
     * @param int $customerId the customer ID
     * @param string[] $headlineAssetResourceNames a list of headline resource names
     * @param string[] $descriptionAssetResourceNames a list of description resource names
     * @return MutateOperation[] a list of MutateOperations that create new asset group assets and
     *     assets
     */
    private static function createAssetandAssetGroupAssetOperations(
        int $customerId,
        array $headlineAssetResourceNames,
        array $descriptionAssetResourceNames
    ): array {
        $operations = [];
        // For the list of required assets for a Performance Max campaign, see
        // https://developers.google.com/google-ads/api/docs/performance-max/assets

        // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset
        // and providing:
        // -  the resource name of the AssetGroup
        // -  the resource name of the Asset
        // -  the field_type of the Asset in this AssetGroup.
        //
        // To learn more about AssetGroups, see
        // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups.

        // Links the previously created multiple text assets.

        // Links the headline assets.
        foreach ($headlineAssetResourceNames as $resourceName) {
            $operations[] = new MutateOperation([
                'asset_group_asset_operation' => new AssetGroupAssetOperation([
                    'create' => new AssetGroupAsset([
                        'asset' => $resourceName,
                        'asset_group' => ResourceNames::forAssetGroup(
                            $customerId,
                            self::ASSET_GROUP_TEMPORARY_ID
                        ),
                        'field_type' => AssetFieldType::HEADLINE
                    ])
                ])
            ]);
        }
        // Links the description assets.
        foreach ($descriptionAssetResourceNames as $resourceName) {
            $operations[] = new MutateOperation([
                'asset_group_asset_operation' => new AssetGroupAssetOperation([
                    'create' => new AssetGroupAsset([
                        'asset' => $resourceName,
                        'asset_group' => ResourceNames::forAssetGroup(
                            $customerId,
                            self::ASSET_GROUP_TEMPORARY_ID
                        ),
                        'field_type' => AssetFieldType::DESCRIPTION
                    ])
                ])
            ]);
        }

        // Creates and links the long headline text asset.
        $operations = array_merge($operations, self::createAndLinkTextAsset(
            $customerId,
            'Travel the World',
            AssetFieldType::LONG_HEADLINE
        ));
        // Creates and links the business name text asset.
        $operations = array_merge($operations, self::createAndLinkTextAsset(
            $customerId,
            'Interplanetary Cruises',
            AssetFieldType::BUSINESS_NAME
        ));

        // Creates and links the image assets.

        // Creates and links the Logo Asset.
        $operations = array_merge($operations, self::createAndLinkImageAsset(
            $customerId,
            'https://gaagl.page.link/1Crm',
            AssetFieldType::LOGO,
            'Marketing Logo'
        ));
        // Creates and links the Marketing Image Asset.
        $operations = array_merge($operations, self::createAndLinkImageAsset(
            $customerId,
            'https://gaagl.page.link/Eit5',
            AssetFieldType::MARKETING_IMAGE,
            'Marketing Image'
        ));
        // Creates and links the Square Marketing Image Asset.
        $operations = array_merge($operations, self::createAndLinkImageAsset(
            $customerId,
            'https://gaagl.page.link/bjYi',
            AssetFieldType::SQUARE_MARKETING_IMAGE,
            'Square Marketing Image'
        ));

        // After being created the list must be sorted so that all asset operations come before all
        // the asset group asset operations, otherwise the API will reject the request.
        return self::sortAssetAndAssetGroupAssetOperations($operations);
    }

    /**
     * Creates a list of MutateOperations that create a new linked text asset.
     *
     * @param int $customerId the customer ID
     * @param string $text the text of the asset to be created
     * @param int $fieldType the field type of the new asset in the AssetGroupAsset
     * @return MutateOperation[] a list of MutateOperations that create a new linked text asset
     */
    private static function createAndLinkTextAsset(
        int $customerId,
        string $text,
        int $fieldType
    ): array {
        $operations = [];
        // Creates a new mutate operation that creates a text asset.
        $operations[] = new MutateOperation([
            'asset_operation' => new AssetOperation([
                'create' => new Asset([
                    'resource_name' => ResourceNames::forAsset($customerId, self::$nextTempId),
                    'text_asset' => new TextAsset(['text' => $text])
                ])
            ])
        ]);

        // Creates an asset group asset to link the asset to the asset group.
        $operations[] = new MutateOperation([
            'asset_group_asset_operation' => new AssetGroupAssetOperation([
                'create' => new AssetGroupAsset([
                    'asset' => ResourceNames::forAsset($customerId, self::$nextTempId),
                    'asset_group' => ResourceNames::forAssetGroup(
                        $customerId,
                        self::ASSET_GROUP_TEMPORARY_ID
                    ),
                    'field_type' => $fieldType
                ])
            ])
        ]);
        self::$nextTempId--;

        return $operations;
    }

    /**
     * Creates a list of MutateOperations that create a new linked image asset.
     *
     * @param int $customerId the customer ID
     * @param string $url the URL of the image to be retrieved and put into an asset
     * @param int $fieldType the field type of the new asset in the AssetGroupAsset
     * @param string $assetName the asset name
     * @return MutateOperation[] a list of MutateOperations that create a new linked image asset
     */
    private static function createAndLinkImageAsset(
        int $customerId,
        string $url,
        int $fieldType,
        string $assetName
    ): array {
        $operations = [];
        // Creates a new mutate operation that creates an image asset.
        $operations[] = new MutateOperation([
            'asset_operation' => new AssetOperation([
                'create' => new Asset([
                    'resource_name' => ResourceNames::forAsset($customerId, self::$nextTempId),
                    // Provide a unique friendly name to identify your asset.
                    // When there is an existing image asset with the same content but a different
                    // name, the new name will be dropped silently.
                    'name' => $assetName,
                    'image_asset' => new ImageAsset(['data' => file_get_contents($url)])
                ])
            ])
        ]);

        // Creates an asset group asset to link the asset to the asset group.
        $operations[] = new MutateOperation([
            'asset_group_asset_operation' => new AssetGroupAssetOperation([
                'create' => new AssetGroupAsset([
                    'asset' => ResourceNames::forAsset($customerId, self::$nextTempId),
                    'asset_group' => ResourceNames::forAssetGroup(
                        $customerId,
                        self::ASSET_GROUP_TEMPORARY_ID
                    ),
                    'field_type' => $fieldType
                ])
            ])
        ]);
        self::$nextTempId--;

        return $operations;
    }

    /**
     * Sorts a list of asset and asset group asset operations. This sorts the list such that all
     * asset operations precede all asset group asset operations. If asset group assets are created
     * before assets then an error will be returned by the API.
     *
     * @param MutateOperation[] $operations a list of asset and asset group asset mutate operations
     * @return MutateOperation[] a sorted list of asset and asset group asset mutate operations
     */
    private static function sortAssetAndAssetGroupAssetOperations(array $operations): array
    {
        usort(
            $operations,
            function (MutateOperation $operation1, MutateOperation $operation2) {
                if (!is_null($operation1->getAssetOperation())) {
                    return -1;
                } elseif (!is_null($operation1->getAssetOperation())) {
                    return 0;
                } else {
                    return 1;
                }
            }
        );
        return $operations;
    }


    /**
     * Retrieves the list of customer conversion goals.
     *
     * @param GoogleAdsClient $googleAdsClient the Google Ads API client
     * @param int $customerId the customer ID
     * @return array list of dicts containing the category and origin of customer conversion goals
     */
    private static function getCustomerConversionGoals(
        GoogleAdsClient $googleAdsClient,
        int $customerId
    ): array {
        $customerConversionGoals = [];
        $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
        // Creates a query that retrieves all customer conversion goals.
        $query = 'SELECT customer_conversion_goal.category, customer_conversion_goal.origin ' .
            'FROM customer_conversion_goal';
        // The number of conversion goals is typically less than 50 so we use a search request
        // instead of search stream.
        $response =
            $googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query));

        // Iterates over all rows in all pages and builds the list of conversion goals.
        foreach ($response->iterateAllElements() as $googleAdsRow) {
            /** @var GoogleAdsRow $googleAdsRow */
            $customerConversionGoals[] = [
                'category' => $googleAdsRow->getCustomerConversionGoal()->getCategory(),
                'origin' => $googleAdsRow->getCustomerConversionGoal()->getOrigin()
            ];
        }

        return $customerConversionGoals;
    }

    /**
     * Creates a list of MutateOperations that override customer conversion goals.
     *
     * @param int $customerId the customer ID
     * @param array $customerConversionGoals the list of customer conversion goals that will be
     *      overridden
     * @return MutateOperation[] a list of MutateOperations that update campaign conversion goals
     */
    private static function createConversionGoalOperations(
        int $customerId,
        array $customerConversionGoals
    ): array {
        $operations = [];

        // To override the customer conversion goals, we will change the biddability of each of the
        // customer conversion goals so that only the desired conversion goal is biddable in this
        // campaign.
        foreach ($customerConversionGoals as $customerConversionGoal) {
            $campaignConversionGoal = new CampaignConversionGoal([
                'resource_name' => ResourceNames::forCampaignConversionGoal(
                    $customerId,
                    self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,
                    ConversionActionCategory::name($customerConversionGoal['category']),
                    ConversionOrigin::name($customerConversionGoal['origin'])
                )
            ]);
            // Changes the biddability for the campaign conversion goal.
            // Sets biddability to true for the desired (category, origin).
            // Sets biddability to false for all other conversion goals.
            // Note:
            //  1- It is assumed that this Conversion Action
            //     (category=PURCHASE, origin=WEBSITE) exists in this account.
            //  2- More than one goal can be biddable if desired. This example
            //     shows only one.
            if (
                $customerConversionGoal["category"] === ConversionActionCategory::PURCHASE
                && $customerConversionGoal["origin"] === ConversionOrigin::WEBSITE
            ) {
                $campaignConversionGoal->setBiddable(true);
            } else {
                $campaignConversionGoal->setBiddable(false);
            }

            $operations[] = new MutateOperation([
                'campaign_conversion_goal_operation' => new CampaignConversionGoalOperation([
                    'update' => $campaignConversionGoal,
                    // Sets the update mask on the operation. Here the update mask will be a list
                    // of all the fields that were set on the update object.
                    'update_mask' => FieldMasks::allSetFieldsOf($campaignConversionGoal)
                ])
            ]);
        }

        return $operations;
    }

    /**
     * Prints the details of a MutateGoogleAdsResponse. Parses the "response" oneof field name and
     * uses it to extract the new entity's name and resource name.
     *
     * @param MutateGoogleAdsResponse $mutateGoogleAdsResponse the mutate Google Ads response
     */
    private static function printResponseDetails(
        MutateGoogleAdsResponse $mutateGoogleAdsResponse
    ): void {
        foreach ($mutateGoogleAdsResponse->getMutateOperationResponses() as $response) {
            /** @var MutateOperationResponse $response */
            $getter = Serializer::getGetter($response->getResponse());
            printf(
                "Created a(n) %s with '%s'.%s",
                preg_replace(
                    '/Result$/',
                    '',
                    ucfirst(Serializer::toCamelCase($response->getResponse()))
                ),
                $response->$getter()->getResourceName(),
                PHP_EOL
            );
        }
    }
}

AddPerformanceMaxRetailCampaign::main();

      

پایتون

#!/usr/bin/env python
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""This example shows how to create a Performance Max retail campaign.

This will be created for "All products".

For more information about Performance Max retail campaigns, see
https://developers.google.com/google-ads/api/docs/performance-max/retail

Prerequisites:
- You need to have access to a Merchant Center account. You can find
  instructions to create a Merchant Center account here:
  https://support.google.com/merchants/answer/188924.
  This account must be linked to your Google Ads account. The integration
  instructions can be found at:
  https://developers.google.com/google-ads/api/docs/shopping-ads/merchant-center
- You need your Google Ads account to track conversions. The different ways
  to track conversions can be found here:
  https://support.google.com/google-ads/answer/1722054.
- You must have at least one conversion action in the account. For
  more about conversion actions, see
  https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions
"""


import argparse
from datetime import datetime, timedelta
import sys
from uuid import uuid4

from google.api_core import protobuf_helpers
from google.ads.googleads.client import GoogleAdsClient
from google.ads.googleads.errors import GoogleAdsException
from google.ads.googleads.util import convert_snake_case_to_upper_case

import requests


# We specify temporary IDs that are specific to a single mutate request.
# Temporary IDs are always negative and unique within one mutate request.
#
# See https://developers.google.com/google-ads/api/docs/mutating/best-practices
# for further details.
#
# These temporary IDs are fixed because they are used in multiple places.
_BUDGET_TEMPORARY_ID = "-1"
_PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = "-2"
_ASSET_GROUP_TEMPORARY_ID = "-3"

# There are also entities that will be created in the same request but do not
# need to be fixed temporary IDs because they are referenced only once.
_next_temp_id = int(_ASSET_GROUP_TEMPORARY_ID) - 1


def main(
    client,
    customer_id,
    merchant_center_account_id,
    final_url,
):
    """The main method that creates all necessary entities for the example.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        merchant_center_account_id: The Merchant Center account ID.
        final_url: the final URL.
    """
    googleads_service = client.get_service("GoogleAdsService")

    # This campaign will override the customer conversion goals.
    # Retrieve the current list of customer conversion goals.
    customer_conversion_goals = get_customer_conversion_goals(
        client, customer_id
    )

    # Performance Max campaigns require that repeated assets such as headlines
    # and descriptions be created before the campaign.
    # For the list of required assets for a Performance Max campaign, see
    # https://developers.google.com/google-ads/api/docs/performance-max/assets
    #
    # Create the headlines.
    headline_asset_resource_names = create_multiple_text_assets(
        client,
        customer_id,
        [
            "Travel",
            "Travel Reviews",
            "Book travel",
        ],
    )
    # Create the descriptions.
    description_asset_resource_names = create_multiple_text_assets(
        client,
        customer_id,
        [
            "Take to the air!",
            "Fly to the sky!",
        ],
    )

    # The below methods create and return MutateOperations that we later
    # provide to the GoogleAdsService.Mutate method in order to create the
    # entities in a single request. Since the entities for a Performance Max
    # campaign are closely tied to one-another, it's considered a best practice
    # to create them in a single Mutate request so they all complete
    # successfully or fail entirely, leaving no orphaned entities. See:
    # https://developers.google.com/google-ads/api/docs/mutating/overview
    campaign_budget_operation = create_campaign_budget_operation(
        client,
        customer_id,
    )
    performance_max_campaign_operation = (
        create_performance_max_campaign_operation(
            client,
            customer_id,
            merchant_center_account_id,
        )
    )
    campaign_criterion_operations = create_campaign_criterion_operations(
        client,
        customer_id,
    )
    asset_group_operation = create_asset_group_operation(
        client, customer_id, final_url
    )
    listing_group_filter_operation = create_listing_group_filter_operation(
        client, customer_id
    )
    asset_and_asset_group_asset_operations = (
        create_asset_and_asset_group_asset_operations(
            client,
            customer_id,
            headline_asset_resource_names,
            description_asset_resource_names,
        )
    )
    conversion_goal_operations = create_conversion_goal_operations(
        client,
        customer_id,
        customer_conversion_goals,
    )

    # Send the operations in a single Mutate request.
    response = googleads_service.mutate(
        customer_id=customer_id,
        mutate_operations=[
            # It's important to create these entities in this order because
            # they depend on each other.
            campaign_budget_operation,
            performance_max_campaign_operation,
            # Expand the list of multiple operations into the list of
            # other mutate operations.
            *campaign_criterion_operations,
            asset_group_operation,
            listing_group_filter_operation,
            *asset_and_asset_group_asset_operations,
            *conversion_goal_operations,
        ],
    )
    print_response_details(response)


def create_campaign_budget_operation(
    client,
    customer_id,
):
    """Creates a MutateOperation that creates a new CampaignBudget.

    A temporary ID will be assigned to this campaign budget so that it can be
    referenced by other objects being created in the same Mutate request.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.

    Returns:
        a MutateOperation that creates a CampaignBudget.
    """
    mutate_operation = client.get_type("MutateOperation")
    campaign_budget_operation = mutate_operation.campaign_budget_operation
    campaign_budget = campaign_budget_operation.create
    campaign_budget.name = f"Performance Max retail campaign budget #{uuid4()}"
    # The budget period already defaults to DAILY.
    campaign_budget.amount_micros = 50000000
    campaign_budget.delivery_method = (
        client.enums.BudgetDeliveryMethodEnum.STANDARD
    )
    # A Performance Max campaign cannot use a shared campaign budget.
    campaign_budget.explicitly_shared = False

    # Set a temporary ID in the budget's resource name so it can be referenced
    # by the campaign in later steps.
    campaign_budget.resource_name = client.get_service(
        "CampaignBudgetService"
    ).campaign_budget_path(customer_id, _BUDGET_TEMPORARY_ID)

    return mutate_operation


def create_performance_max_campaign_operation(
    client,
    customer_id,
    merchant_center_account_id,
):
    """Creates a MutateOperation that creates a new Performance Max campaign.

    A temporary ID will be assigned to this campaign so that it can
    be referenced by other objects being created in the same Mutate request.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        merchant_center_account_id: The Merchant Center account ID.

    Returns:
        a MutateOperation that creates a campaign.
    """
    mutate_operation = client.get_type("MutateOperation")
    campaign = mutate_operation.campaign_operation.create
    campaign.name = f"Performance Max retail campaign #{uuid4()}"
    # Set the campaign status as PAUSED. The campaign is the only entity in
    # the mutate request that should have its status set.
    campaign.status = client.enums.CampaignStatusEnum.PAUSED
    # All Performance Max campaigns have an advertising_channel_type of
    # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.
    campaign.advertising_channel_type = (
        client.enums.AdvertisingChannelTypeEnum.PERFORMANCE_MAX
    )
    # Bidding strategy must be set directly on the campaign.
    # Setting a portfolio bidding strategy by resource name is not supported.
    # Max Conversion and Max Conversion Value are the only strategies supported
    # for Performance Max campaigns.
    # An optional ROAS (Return on Advertising Spend) can be set for
    # maximize_conversion_value. The ROAS value must be specified as a ratio in
    # the API. It is calculated by dividing "total value" by "total spend".
    # For more information on Max Conversion Value, see the support article:
    # http://support.google.com/google-ads/answer/7684216.
    # A target_roas of 3.5 corresponds to a 350% return on ad spend.
    # campaign.maximize_conversion_value.target_roas = 3.5
    # For first time users, it's recommended not to set a target ROAS.
    # Although target ROAS is optional, you still need to define it
    # even if you do not want to use it.
    campaign.maximize_conversion_value.target_roas = None
    # Below is what you would use if you want to maximize conversions
    # campaign.maximize_conversions.target_cpa_micros = None
    # The target CPA is optional. This is the average amount that you would
    # like to spend per conversion action.

    # Set the shopping settings.
    campaign.shopping_setting.merchant_id = merchant_center_account_id

    # Optional: To use products only from a specific feed, set
    # shopping_setting.feed_label to the feed label used in Merchant Center.
    # See: https://support.google.com/merchants/answer/12453549.
    # Omitting the shopping_setting.feed_label field will use products from all
    # feeds.
    # campaign.shopping_setting.feed_label = "INSERT_FEED_LABEL_HERE"

    # Set the Final URL expansion opt out. This flag is specific to
    # Performance Max campaigns. If opted out (True), only the final URLs in
    # the asset group or URLs specified in the advertiser's Google Merchant
    # If opted in (False), the entire domain will be targeted. For best
    # results, set this value to false to opt in and allow URL expansions. You
    # can optionally add exclusions to limit traffic to parts of your website.
    # For a Retail campaign, we want the final URL's to be limited to
    # those explicitly surfaced via GMC.
    campaign.url_expansion_opt_out = True

    # Assign the resource name with a temporary ID.
    campaign_service = client.get_service("CampaignService")
    campaign.resource_name = campaign_service.campaign_path(
        customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
    )
    # Set the budget using the given budget resource name.
    campaign.campaign_budget = campaign_service.campaign_budget_path(
        customer_id, _BUDGET_TEMPORARY_ID
    )

    # Optional fields
    campaign.start_date = (datetime.now() + timedelta(1)).strftime("%Y%m%d")
    campaign.end_date = (datetime.now() + timedelta(365)).strftime("%Y%m%d")

    return mutate_operation


def create_campaign_criterion_operations(
    client,
    customer_id,
):
    """Creates a list of MutateOperations that create new campaign criteria.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.

    Returns:
        a list of MutateOperations that create new campaign criteria.
    """
    campaign_service = client.get_service("CampaignService")
    geo_target_constant_service = client.get_service("GeoTargetConstantService")
    googleads_service = client.get_service("GoogleAdsService")

    operations = []
    # Set the LOCATION campaign criteria.
    # Target all of New York City except Brooklyn.
    # Location IDs are listed here:
    # https://developers.google.com/google-ads/api/reference/data/geotargets
    # and they can also be retrieved using the GeoTargetConstantService as shown
    # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting
    mutate_operation = client.get_type("MutateOperation")
    campaign_criterion = mutate_operation.campaign_criterion_operation.create
    campaign_criterion.campaign = campaign_service.campaign_path(
        customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
    )
    # Adds one positive location target for New York City (ID=1023191),
    # specifically adding the positive criteria before the negative one.
    campaign_criterion.location.geo_target_constant = (
        geo_target_constant_service.geo_target_constant_path("1023191")
    )
    campaign_criterion.negative = False
    operations.append(mutate_operation)

    # Next add the negative target for Brooklyn (ID=1022762).
    mutate_operation = client.get_type("MutateOperation")
    campaign_criterion = mutate_operation.campaign_criterion_operation.create
    campaign_criterion.campaign = campaign_service.campaign_path(
        customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
    )
    campaign_criterion.location.geo_target_constant = (
        geo_target_constant_service.geo_target_constant_path("1022762")
    )
    campaign_criterion.negative = True
    operations.append(mutate_operation)

    # Set the LANGUAGE campaign criterion.
    mutate_operation = client.get_type("MutateOperation")
    campaign_criterion = mutate_operation.campaign_criterion_operation.create
    campaign_criterion.campaign = campaign_service.campaign_path(
        customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
    )
    # Set the language.
    # For a list of all language codes, see:
    # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7
    campaign_criterion.language.language_constant = (
        googleads_service.language_constant_path("1000")
    )  # English
    operations.append(mutate_operation)

    return operations


def create_multiple_text_assets(client, customer_id, texts):
    """Creates multiple text assets and returns the list of resource names.

    These repeated assets must be created in a separate request prior to
    creating the campaign.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        texts: a list of strings, each of which will be used to create a text
          asset.

    Returns:
        asset_resource_names: a list of asset resource names.
    """
    # Here again we use the GoogleAdService to create multiple text
    # assets in a single request.
    googleads_service = client.get_service("GoogleAdsService")

    operations = []
    for text in texts:
        mutate_operation = client.get_type("MutateOperation")
        asset = mutate_operation.asset_operation.create
        asset.text_asset.text = text
        operations.append(mutate_operation)

    # Send the operations in a single Mutate request.
    response = googleads_service.mutate(
        customer_id=customer_id,
        mutate_operations=operations,
    )
    asset_resource_names = []
    for result in response.mutate_operation_responses:
        if result._pb.HasField("asset_result"):
            asset_resource_names.append(result.asset_result.resource_name)
    print_response_details(response)
    return asset_resource_names


def create_asset_group_operation(client, customer_id, final_url):
    """Creates a MutateOperation that creates a new asset group.

    A temporary ID will be assigned to this asset group so that it can
    be referenced by other objects being created in the same Mutate request.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        final_url: the final URL.

    Returns:
        a MutateOperation that creates a new asset group.
    """
    googleads_service = client.get_service("GoogleAdsService")
    # Create the AssetGroup.
    mutate_operation = client.get_type("MutateOperation")
    asset_group = mutate_operation.asset_group_operation.create
    asset_group.name = f"Performance Max retail asset group #{uuid4()}"
    asset_group.campaign = googleads_service.campaign_path(
        customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
    )
    asset_group.final_urls.append(final_url)
    asset_group.final_mobile_urls.append(final_url)
    asset_group.status = client.enums.AssetGroupStatusEnum.PAUSED
    asset_group.resource_name = googleads_service.asset_group_path(
        customer_id,
        _ASSET_GROUP_TEMPORARY_ID,
    )

    return mutate_operation


def create_listing_group_filter_operation(client, customer_id):
    """Creates a MutateOperation that creates a new listing group filter.

    A temporary ID will be assigned to this listing group filter so that it
    can be referenced by other objects being created in the same Mutate request.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.

    Returns:
        a MutateOperation that creates a new listing group filter.
    """
    googleads_service = client.get_service("GoogleAdsService")
    # Creates a new ad group criterion containing the "default" listing
    # group (All products).
    mutate_operation = client.get_type("MutateOperation")
    asset_group_listing_group = (
        mutate_operation.asset_group_listing_group_filter_operation.create
    )
    asset_group_listing_group.asset_group = googleads_service.asset_group_path(
        customer_id,
        _ASSET_GROUP_TEMPORARY_ID,
    )
    asset_group_listing_group.type_ = (
        client.enums.ListingGroupFilterTypeEnum.UNIT_INCLUDED
    )
    # Because this is a Performance Max campaign for retail, we need to specify
    # that this is in the shopping listing source.
    asset_group_listing_group.listing_source = (
        client.enums.ListingGroupFilterListingSourceEnum.SHOPPING
    )

    return mutate_operation


def create_asset_and_asset_group_asset_operations(
    client,
    customer_id,
    headline_asset_resource_names,
    description_asset_resource_names,
):
    """Creates a list of MutateOperations that create a new asset_group.

    A temporary ID will be assigned to this asset group so that it can
    be referenced by other objects being created in the same Mutate request.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        headline_asset_resource_names: a list of headline resource names.
        description_asset_resource_names: a list of description resource names.

    Returns:
        MutateOperations that create a new asset group and related assets.
    """
    asset_group_service = client.get_service("AssetGroupService")

    operations = []

    # For the list of required assets for a Performance Max campaign, see
    # https://developers.google.com/google-ads/api/docs/performance-max/assets

    # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset
    # and providing:
    #   the resource name of the AssetGroup
    #   the resource name of the Asset
    #   the field_type of the Asset in this AssetGroup.
    #
    # To learn more about AssetGroups, see
    # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups

    # Link the previously created multiple text assets.

    # Link the headline assets.
    for resource_name in headline_asset_resource_names:
        mutate_operation = client.get_type("MutateOperation")
        asset_group_asset = mutate_operation.asset_group_asset_operation.create
        asset_group_asset.field_type = client.enums.AssetFieldTypeEnum.HEADLINE
        asset_group_asset.asset_group = asset_group_service.asset_group_path(
            customer_id,
            _ASSET_GROUP_TEMPORARY_ID,
        )
        asset_group_asset.asset = resource_name
        operations.append(mutate_operation)

    #  Link the description assets.
    for resource_name in description_asset_resource_names:
        mutate_operation = client.get_type("MutateOperation")
        asset_group_asset = mutate_operation.asset_group_asset_operation.create
        asset_group_asset.field_type = (
            client.enums.AssetFieldTypeEnum.DESCRIPTION
        )
        asset_group_asset.asset_group = asset_group_service.asset_group_path(
            customer_id,
            _ASSET_GROUP_TEMPORARY_ID,
        )
        asset_group_asset.asset = resource_name
        operations.append(mutate_operation)

    # Create and link the long headline text asset.
    mutate_operations = create_and_link_text_asset(
        client,
        customer_id,
        "Travel the World",
        client.enums.AssetFieldTypeEnum.LONG_HEADLINE,
    )
    operations.extend(mutate_operations)

    # Create and link the business name text asset.
    mutate_operations = create_and_link_text_asset(
        client,
        customer_id,
        "Interplanetary Cruises",
        client.enums.AssetFieldTypeEnum.BUSINESS_NAME,
    )
    operations.extend(mutate_operations)

    # Create and link the image assets.

    # Create and link the Logo Asset.
    mutate_operations = create_and_link_image_asset(
        client,
        customer_id,
        "https://gaagl.page.link/1Crm",
        client.enums.AssetFieldTypeEnum.LOGO,
        "Logo Image",
    )
    operations.extend(mutate_operations)

    # Create and link the Marketing Image Asset.
    mutate_operations = create_and_link_image_asset(
        client,
        customer_id,
        "https://gaagl.page.link/Eit5",
        client.enums.AssetFieldTypeEnum.MARKETING_IMAGE,
        "Marketing Image",
    )
    operations.extend(mutate_operations)

    # Create and link the Square Marketing Image Asset.
    mutate_operations = create_and_link_image_asset(
        client,
        customer_id,
        "https://gaagl.page.link/bjYi",
        client.enums.AssetFieldTypeEnum.SQUARE_MARKETING_IMAGE,
        "Square Marketing Image",
    )
    operations.extend(mutate_operations)

    # After being created the list must be sorted so that all asset
    # operations come before all the asset group asset operations,
    # otherwise the API will reject the request.
    return sort_asset_and_asset_group_asset_operations(operations)


def create_and_link_text_asset(client, customer_id, text, field_type):
    """Creates a list of MutateOperations that create a new linked text asset.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        text: the text of the asset to be created.
        field_type: the field_type of the new asset in the AssetGroupAsset.

    Returns:
        MutateOperations that create a new linked text asset.
    """
    global _next_temp_id
    operations = []
    asset_service = client.get_service("AssetService")
    asset_group_service = client.get_service("AssetGroupService")

    # Create the Text Asset.
    mutate_operation = client.get_type("MutateOperation")
    asset = mutate_operation.asset_operation.create
    asset.resource_name = asset_service.asset_path(customer_id, _next_temp_id)
    asset.text_asset.text = text
    operations.append(mutate_operation)

    # Create an AssetGroupAsset to link the Asset to the AssetGroup.
    mutate_operation = client.get_type("MutateOperation")
    asset_group_asset = mutate_operation.asset_group_asset_operation.create
    asset_group_asset.field_type = field_type
    asset_group_asset.asset_group = asset_group_service.asset_group_path(
        customer_id,
        _ASSET_GROUP_TEMPORARY_ID,
    )
    asset_group_asset.asset = asset_service.asset_path(
        customer_id, _next_temp_id
    )
    operations.append(mutate_operation)

    _next_temp_id -= 1
    return operations


def create_and_link_image_asset(
    client, customer_id, url, field_type, asset_name
):
    """Creates a list of MutateOperations that create a new linked image asset.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        url: the url of the image to be retrieved and put into an asset.
        field_type: the field_type of the new asset in the AssetGroupAsset.
        asset_name: the asset name.

    Returns:
        MutateOperations that create a new linked image asset.
    """
    global _next_temp_id
    operations = []
    asset_service = client.get_service("AssetService")
    asset_group_service = client.get_service("AssetGroupService")

    # Create the Image Asset.
    mutate_operation = client.get_type("MutateOperation")
    asset = mutate_operation.asset_operation.create
    asset.resource_name = asset_service.asset_path(customer_id, _next_temp_id)
    asset.type_ = client.enums.AssetTypeEnum.IMAGE
    # Provide a unique friendly name to identify your asset.
    # When there is an existing image asset with the same content but a different
    # name, the new name will be dropped silently.
    asset.name = asset_name
    asset.image_asset.data = get_image_bytes(url)
    operations.append(mutate_operation)

    # Create an AssetGroupAsset to link the Asset to the AssetGroup.
    mutate_operation = client.get_type("MutateOperation")
    asset_group_asset = mutate_operation.asset_group_asset_operation.create
    asset_group_asset.field_type = field_type
    asset_group_asset.asset_group = asset_group_service.asset_group_path(
        customer_id,
        _ASSET_GROUP_TEMPORARY_ID,
    )
    asset_group_asset.asset = asset_service.asset_path(
        customer_id, _next_temp_id
    )
    operations.append(mutate_operation)

    _next_temp_id -= 1
    return operations


def sort_asset_and_asset_group_asset_operations(operations):
    """Sorts a list of asset and asset group asset operations.

    This sorts the list such that all asset operations precede
    all asset group asset operations. If asset group assets are
    created before assets then an error will be returned by
    the API.

    Args:
        operations: a list of asset and asset group asset operations.

    Returns:
        a sorted list of asset and asset group asset operations.
    """

    def sorter(operation):
        """Determines whether the operation creates an asset group asset.

        Args:
            operation: a MutateOperation instance.

        Returns:
            True if the MutateOperation creates an asset group asset.
        """
        return bool(operation.asset_group_asset_operation)

    return sorted(operations, key=sorter)


def get_customer_conversion_goals(client, customer_id):
    """Retrieves the list of customer conversion goals.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.

    Returns:
        a list of dicts containing the category and origin of customer
        conversion goals.
    """
    ga_service = client.get_service("GoogleAdsService")
    customer_conversion_goals = []
    query = """
            SELECT
              customer_conversion_goal.category,
              customer_conversion_goal.origin
            FROM customer_conversion_goal
            """
    # The number of conversion goals is typically less than 50 so we use
    # GoogleAdsService.search instead of search_stream.
    search_request = client.get_type("SearchGoogleAdsRequest")
    search_request.customer_id = customer_id
    search_request.query = query
    results = ga_service.search(request=search_request)

    # Iterate over the results and build the list of conversion goals.
    for row in results:
        customer_conversion_goals.append(
            {
                "category": row.customer_conversion_goal.category,
                "origin": row.customer_conversion_goal.origin,
            }
        )
    return customer_conversion_goals


def create_conversion_goal_operations(
    client,
    customer_id,
    customer_conversion_goals,
):
    """Creates a list of MutateOperations that override customer conversion goals.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        customer_conversion_goals: the list of customer conversion goals that
          will be overridden.

    Returns:
        MutateOperations that update campaign conversion goals.
    """
    campaign_conversion_goal_service = client.get_service(
        "CampaignConversionGoalService"
    )
    operations = []

    # To override the customer conversion goals, we will change the
    # biddability of each of the customer conversion goals so that only
    # the desired conversion goal is biddable in this campaign.
    for customer_conversion_goal in customer_conversion_goals:
        mutate_operation = client.get_type("MutateOperation")
        campaign_conversion_goal = (
            mutate_operation.campaign_conversion_goal_operation.update
        )

        campaign_conversion_goal.resource_name = (
            campaign_conversion_goal_service.campaign_conversion_goal_path(
                customer_id,
                _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,
                customer_conversion_goal["category"].name,
                customer_conversion_goal["origin"].name,
            )
        )
        # Change the biddability for the campaign conversion goal.
        # Set biddability to True for the desired (category, origin).
        # Set biddability to False for all other conversion goals.
        # Note:
        #  1- It is assumed that this Conversion Action
        #     (category=PURCHASE, origin=WEBSITE) exists in this account.
        #  2- More than one goal can be biddable if desired. This example
        #     shows only one.
        if (
            customer_conversion_goal["category"]
            == client.enums.ConversionActionCategoryEnum.PURCHASE
            and customer_conversion_goal["origin"]
            == client.enums.ConversionOriginEnum.WEBSITE
        ):
            biddable = True
        else:
            biddable = False
        campaign_conversion_goal.biddable = biddable
        field_mask = protobuf_helpers.field_mask(
            None, campaign_conversion_goal._pb
        )
        client.copy_from(
            mutate_operation.campaign_conversion_goal_operation.update_mask,
            field_mask,
        )
        operations.append(mutate_operation)

    return operations


def get_image_bytes(url):
    """Loads image data from a URL.

    Args:
        url: a URL str.

    Returns:
        Images bytes loaded from the given URL.
    """
    response = requests.get(url)
    return response.content


def print_response_details(response):
    """Prints the details of a MutateGoogleAdsResponse.

    Parses the "response" oneof field name and uses it to extract the new
    entity's name and resource name.

    Args:
        response: a MutateGoogleAdsResponse object.
    """
    # Parse the Mutate response to print details about the entities that
    # were created by the request.
    suffix = "_result"
    for result in response.mutate_operation_responses:
        for field_descriptor, value in result._pb.ListFields():
            if field_descriptor.name.endswith(suffix):
                name = field_descriptor.name[: -len(suffix)]
            else:
                name = field_descriptor.name
            print(
                f"Created a(n) {convert_snake_case_to_upper_case(name)} with "
                f"{str(value).strip()}."
            )



if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description=("Creates a Performance Max retail campaign.")
    )
    # The following argument(s) should be provided to run the example.
    parser.add_argument(
        "-c",
        "--customer_id",
        type=str,
        required=True,
        help="The Google Ads customer ID.",
    )
    parser.add_argument(
        "-m",
        "--merchant_center_account_id",
        type=int,
        required=True,
        help="The Merchant Center account ID.",
    )
    parser.add_argument(
        "-u",
        "--final_url",
        type=str,
        required=False,
        default="http://www.example.com",
        help="The final URL for the asset group of the campaign.",
    )

    args = parser.parse_args()

    # GoogleAdsClient will read the google-ads.yaml configuration file in the
    # home directory if none is specified.
    googleads_client = GoogleAdsClient.load_from_storage(version="v17")

    try:
        main(
            googleads_client,
            args.customer_id,
            args.merchant_center_account_id,
            args.final_url,
        )
    except GoogleAdsException as ex:
        print(
            f'Request with ID "{ex.request_id}" failed with status '
            f'"{ex.error.code().name}" and includes the following errors:'
        )
        for error in ex.failure.errors:
            print(f'Error with message "{error.message}".')
            if error.location:
                for field_path_element in error.location.field_path_elements:
                    print(f"\t\tOn field: {field_path_element.field_name}")
        sys.exit(1)

      

روبی

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This example shows how to create a Performance Max retail campaign.
#
# This will be created for "All products".
#
# For more information about Performance Max retail campaigns, see
# https://developers.google.com/google-ads/api/docs/performance-max/retail
#
# Prerequisites:
# - You need to have access to a Merchant Center account. You can find
#   instructions to create a Merchant Center account here:
#   https://support.google.com/merchants/answer/188924.
#   This account must be linked to your Google Ads account. The integration
#   instructions can be found at:
#   https://developers.google.com/google-ads/api/docs/shopping-ads/merchant-center
# - You need your Google Ads account to track conversions. The different ways
#   to track conversions can be found here:
#   https://support.google.com/google-ads/answer/1722054.
# - You must have at least one conversion action in the account. For
#   more about conversion actions, see
#   https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions


require 'optparse'
require 'date'
require 'open-uri'
require 'google/ads/google_ads'

# We specify temporary IDs that are specific to a single mutate request.
# Temporary IDs are always negative and unique within one mutate request.
#
# See https://developers.google.com/google-ads/api/docs/mutating/best-practices
# for further details.
#
# These temporary IDs are fixed because they are used in multiple places.
BUDGET_TEMPORARY_ID = "-1"
PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = "-2"
ASSET_GROUP_TEMPORARY_ID = "-3"

# There are also entities that will be created in the same request but do not
# need to be fixed temporary IDs because they are referenced only once.
def next_temp_id
  @id ||= ASSET_GROUP_TEMPORARY_ID.to_i
  @id -= 1
end

def add_performance_max_retail_campaign(
    customer_id,
    merchant_center_account_id,
    final_url)
  # GoogleAdsClient will read a config file from
  # ENV['HOME']/google_ads_config.rb when called without parameters
  client = Google::Ads::GoogleAds::GoogleAdsClient.new

  # This campaign will override the customer conversion goals.
  # Retrieve the current list of customer conversion goals.
  customer_conversion_goals = _get_customer_conversion_goals(
      client, customer_id)

  # Performance Max campaigns require that repeated assets such as headlines
  # and descriptions be created before the campaign.
  # For the list of required assets for a Performance Max campaign, see
  # https://developers.google.com/google-ads/api/docs/performance-max/assets
  #
  # Create the headlines.
  headline_asset_resource_names = create_multiple_text_assets(
    client,
    customer_id,
    [
      "Travel",
      "Travel Reviews",
      "Book travel",
    ])
  # Create the descriptions.
  description_asset_resource_names = create_multiple_text_assets(
    client,
    customer_id,
    [
      "Take to the air!",
      "Fly to the sky!",
    ])

  # The below methods create and return MutateOperations that we later
  # provide to the GoogleAdsService.Mutate method in order to create the
  # entities in a single request. Since the entities for a Performance Max
  # campaign are closely tied to one-another, it's considered a best practice
  # to create them in a single Mutate request so they all complete
  # successfully or fail entirely, leaving no orphaned entities. See:
  # https://developers.google.com/google-ads/api/docs/mutating/overview
  campaign_budget_operation = create_campaign_budget_operation(
    client,
    customer_id,
  )
  performance_max_campaign_operation = create_performance_max_campaign_operation(
    client,
    customer_id,
    merchant_center_account_id,
  )
  campaign_criterion_operations = create_campaign_criterion_operations(
    client,
    customer_id,
  )
  asset_group_operation = create_asset_group_operation(
    client,
    customer_id,
    final_url,
  )
  listing_group_filter_operation = create_listing_group_filter_operation(
    client,
    customer_id,
  )
  asset_and_asset_group_asset_operations = create_asset_and_asset_group_asset_operations(
    client,
    customer_id,
    headline_asset_resource_names,
    description_asset_resource_names,
  )
  conversion_goal_operations = create_conversion_goal_operations(
    client,
    customer_id,
    customer_conversion_goals,
  )

  # Send the operations in a single Mutate request.
  response = client.service.google_ads.mutate(
    customer_id: customer_id,
    mutate_operations: [
      # It's important to create these entities in this order because
      # they depend on each other.
      campaign_budget_operation,
      performance_max_campaign_operation,
      # Expand the list of multiple operations into the list of
      # other mutate operations
      campaign_criterion_operations,
      asset_group_operation,
      listing_group_filter_operation,
      asset_and_asset_group_asset_operations,
      conversion_goal_operations,
    ].flatten)

  print_response_details(response)
end

# Creates a MutateOperation that creates a new CampaignBudget.
#
# A temporary ID will be assigned to this campaign budget so that it can be
# referenced by other objects being created in the same Mutate request.
def create_campaign_budget_operation(client, customer_id)
    client.operation.mutate do |m|
      m.campaign_budget_operation = client.operation.create_resource.campaign_budget do |cb|
        cb.name = "Performance Max campaign budget #{SecureRandom.uuid}"
        # The budget period already defaults to DAILY.
        cb.amount_micros = 50_000_000
        cb.delivery_method = :STANDARD
          # A Performance Max campaign cannot use a shared campaign budget.
          cb.explicitly_shared = false

        # Set a temporary ID in the budget's resource name so it can be referenced
        # by the campaign in later steps.
        cb.resource_name = client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)
      end
    end
  end

# Creates a MutateOperation that creates a new Performance Max campaign.
#
# A temporary ID will be assigned to this campaign so that it can
# be referenced by other objects being created in the same Mutate request.
def create_performance_max_campaign_operation(
    client,
    customer_id,
    merchant_center_account_id)
    client.operation.mutate do |m|
      m.campaign_operation = client.operation.create_resource.campaign do |c|
        c.name = "Performance Max retail campaign #{SecureRandom.uuid}"
        # Set the campaign status as PAUSED. The campaign is the only entity in
        # the mutate request that should have its status set.
        c.status = :PAUSED
        # All Performance Max campaigns have an advertising_channel_type of
        # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.
        c.advertising_channel_type = :PERFORMANCE_MAX
        # Bidding strategy must be set directly on the campaign.
        # Setting a portfolio bidding strategy by resource name is not supported.
        # Max Conversion and Maximize Conversion Value are the only strategies
        # supported for Performance Max campaigns.
        # An optional ROAS (Return on Advertising Spend) can be set for
        # maximize_conversion_value. The ROAS value must be specified as a ratio in
        # the API. It is calculated by dividing "total value" by "total spend".
        # For more information on Maximize Conversion Value, see the support
        # article: http://support.google.com/google-ads/answer/7684216.
        # A target_roas of 3.5 corresponds to a 350% return on ad spend.
        c.bidding_strategy_type = :MAXIMIZE_CONVERSION_VALUE
        c.maximize_conversion_value = client.resource.maximize_conversion_value do |mcv|
          mcv.target_roas = 3.5
        end

        # Set the shopping settings.
        c.shopping_setting = client.resource.shopping_setting do |ss|
            ss.merchant_id = merchant_center_account_id
            # Optional: To use products only from a specific feed, set feed_label
            # to the feed label used in Merchant Center.
            # See: https://support.google.com/merchants/answer/12453549.
            # Omitting the feed_label field will use products from all feeds.
            # feed_label = "INSERT_FEED_LABEL_HERE"
        end

        # Set the Final URL expansion opt out. This flag is specific to
        # Performance Max campaigns. If opted out (true), only the final URLs in
        # the asset group or URLs specified in the advertiser's Google Merchant
        # Center or business data feeds are targeted.
        # If opted in (false), the entire domain will be targeted. For best
        # results, set this value to false to opt in and allow URL expansions. You
        # can optionally add exclusions to limit traffic to parts of your website.
        #
        # For a Retail campaign, we want the final URLs to be limited to those
        # explicitly surfaced via GMC.
        c.url_expansion_opt_out = true

        # Assign the resource name with a temporary ID.
        c.resource_name = client.path.campaign(customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)
        # Set the budget using the given budget resource name.
        c.campaign_budget = client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)

        # Optional fields
        c.start_date = DateTime.parse((Date.today + 1).to_s).strftime('%Y%m%d')
        c.end_date = DateTime.parse(Date.today.next_year.to_s).strftime('%Y%m%d')
      end
    end
  end

# Creates a list of MutateOperations that create new campaign criteria.
def create_campaign_criterion_operations(client, customer_id)
    operations = []

    # Set the LOCATION campaign criteria.
    # Target all of New York City except Brooklyn.
    # Location IDs are listed here:
    # https://developers.google.com/google-ads/api/reference/data/geotargets
    # and they can also be retrieved using the GeoTargetConstantService as shown
    # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting
    operations << client.operation.mutate do |m|
      m.campaign_criterion_operation =
        client.operation.create_resource.campaign_criterion do |cc|
        cc.campaign = client.path.campaign(
          customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)
        # Adds one positive location target for New York City (ID=1023191),
        # specifically adding the positive criteria before the negative one.
        cc.location = client.resource.location_info do  |li|
          li.geo_target_constant = client.path.geo_target_constant("1023191")
        end
        cc.negative = false
      end
    end

  # Next add the negative target for Brooklyn (ID=1022762).
  operations << client.operation.mutate do |m|
    m.campaign_criterion_operation =
      client.operation.create_resource.campaign_criterion do |cc|
      cc.campaign = client.path.campaign(
        customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)
      cc.location = client.resource.location_info do  |li|
        li.geo_target_constant = client.path.geo_target_constant("1022762")
      end
      cc.negative = true
    end
  end

  # Set the LANGUAGE campaign criterion.
  operations << client.operation.mutate do |m|
    m.campaign_criterion_operation =
      client.operation.create_resource.campaign_criterion do |cc|
      cc.campaign = client.path.campaign(
        customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)
      # Set the language.
      # For a list of all language codes, see:
      # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7
      cc.language = client.resource.language_info do |li|
        li.language_constant = client.path.language_constant("1000")  # English
      end
    end
  end

  operations
end

# Creates multiple text assets and returns the list of resource names.
# These repeated assets must be created in a separate request prior to creating
# the campaign.
def create_multiple_text_assets(client, customer_id, texts)
  operations = texts.map do |text|
    client.operation.mutate do |m|
      m.asset_operation = client.operation.create_resource.asset do |asset|
        asset.text_asset = client.resource.text_asset do |text_asset|
          text_asset.text = text
        end
      end
    end
  end

  # Send the operations in a single Mutate request.
  response = client.service.google_ads.mutate(
    customer_id: customer_id,
    mutate_operations: operations,
  )

  asset_resource_names = []
  response.mutate_operation_responses.each do |result|
    if result.asset_result
      asset_resource_names.append(result.asset_result.resource_name)
    end
  end
  print_response_details(response)
  asset_resource_names
end

# Creates a MutateOperation that creates a new asset_group.
#
# A temporary ID will be assigned to this asset group so that it can
# be referenced by other objects being created in the same Mutate request.
def create_asset_group_operation(
    client,
    customer_id,
    final_url)

  # Create the AssetGroup
  client.operation.mutate do |m|
    m.asset_group_operation = client.operation.create_resource.asset_group do |ag|
      ag.name = "Performance Max retail asset group #{SecureRandom.uuid}"
      ag.campaign = client.path.campaign(
        customer_id,
        PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)
      ag.final_urls << final_url
      ag.final_mobile_urls << final_url
      ag.status = :PAUSED
      ag.resource_name = client.path.asset_group(
        customer_id,
        ASSET_GROUP_TEMPORARY_ID,
      )
    end
  end
end

# Creates a MutateOperation that creates a new listing group filter.
# A temporary ID will be assigned to this listing group filter so that it can
# be referenced by other objects being created in the same Mutate request.
def create_listing_group_filter_operation(client, customer_id)
  client.operation.mutate do |m|
    m.asset_group_listing_group_filter_operation =
      client.operation.create_resource.asset_group_listing_group_filter do |aglg|
        aglg.asset_group = client.path.asset_group(
          customer_id,
          ASSET_GROUP_TEMPORARY_ID,
        )
        aglg.type = :UNIT_INCLUDED
        # Because this is a Performance Max campaign for retail, we need to
        # specify that this is in the shopping listing source.
        aglg.listing_source = :SHOPPING
      end
  end
end

# Creates a list of MutateOperations that create a new asset_group.
# A temporary ID will be assigned to this asset group so that it can be
# referenced by other objects being created in the same Mutate request.
def create_asset_and_asset_group_asset_operations(
  client,
  customer_id,
  headline_asset_resource_names,
  description_asset_resource_names)
  operations = []
  # For the list of required assets for a Performance Max campaign, see
  # https://developers.google.com/google-ads/api/docs/performance-max/assets
  #
  # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset
  # and providing:
  #   the resource name of the AssetGroup
  #   the resource name of the Asset
  #   the field_type of the Asset in this AssetGroup.
  #
  # To learn more about AssetGroups, see
  # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups

  # Link the previously created multiple text assets.

  # Link the headline assets.
  headline_asset_resource_names.each do |resource_name|
    operations << client.operation.mutate do |m|
      m.asset_group_asset_operation = client.operation.create_resource
          .asset_group_asset do |aga|
        aga.field_type = :HEADLINE
        aga.asset_group = client.path.asset_group(
          customer_id,
          ASSET_GROUP_TEMPORARY_ID)
        aga.asset = resource_name
      end
    end
  end

  #  Link the description assets.
  description_asset_resource_names.each do |resource_name|
    operations << client.operation.mutate do |m|
      m.asset_group_asset_operation = client.operation.create_resource
          .asset_group_asset do |aga|
        aga.field_type = :DESCRIPTION
        aga.asset_group = client.path.asset_group(
          customer_id,
          ASSET_GROUP_TEMPORARY_ID)
        aga.asset = resource_name
      end
    end
  end

  # Create and link the long headline text asset.
  operations += create_and_link_text_asset(
    client,
    customer_id,
    "Travel the World",
    :LONG_HEADLINE)

  # Create and link the business name text asset.
  operations += create_and_link_text_asset(
    client,
    customer_id,
    "Interplanetary Cruises",
    :BUSINESS_NAME)

  # Create and link the image assets.

  # Create and link the Logo Asset.
  operations += create_and_link_image_asset(
    client,
    customer_id,
    "https://gaagl.page.link/1Crm",
    :LOGO,
    "Logo Image")

  # Create and link the Marketing Image Asset.
  operations += create_and_link_image_asset(
    client,
    customer_id,
    "https://gaagl.page.link/Eit5",
    :MARKETING_IMAGE,
    "Marketing Image")

  # Create and link the Square Marketing Image Asset.
  operations += create_and_link_image_asset(
    client,
    customer_id,
    "https://gaagl.page.link/bjYi",
    :SQUARE_MARKETING_IMAGE,
    "Square Marketing Image")

  # After being created the list must be sorted so that all asset
  # operations come before all the asset group asset operations,
  # otherwise the API will reject the request.
  sort_asset_and_asset_group_asset_operations(operations)
end

# Creates a list of MutateOperations that create a new linked text asset.
def create_and_link_text_asset(client, customer_id, text, field_type)
  operations = []
  temp_id = next_temp_id

  # Create the Text Asset.
  operations << client.operation.mutate do |m|
    m.asset_operation = client.operation.create_resource.asset do |a|
      a.resource_name = client.path.asset(customer_id, temp_id)
      a.text_asset = client.resource.text_asset do |text_asset|
        text_asset.text = text
      end
    end
  end

  # Create an AssetGroupAsset to link the Asset to the AssetGroup.
  operations << client.operation.mutate do |m|
    m.asset_group_asset_operation = client.operation.create_resource
        .asset_group_asset do |aga|
      aga.field_type = field_type
      aga.asset_group = client.path.asset_group(
        customer_id,
        ASSET_GROUP_TEMPORARY_ID)
      aga.asset = client.path.asset(customer_id, temp_id)
    end
  end

  operations
end

# Creates a list of MutateOperations that create a new linked image asset.
def create_and_link_image_asset(client, customer_id, url, field_type, asset_name)
  operations = []
  temp_id = next_temp_id

  # Create the Image Asset.
  operations << client.operation.mutate do |m|
    m.asset_operation = client.operation.create_resource.asset do |a|
      a.resource_name = client.path.asset(customer_id, temp_id)
      a.type = :IMAGE
      # Provide a unique friendly name to identify your asset.
      # When there is an existing image asset with the same content but a different
      # name, the new name will be dropped silently.
      a.name = asset_name
      a.image_asset = client.resource.image_asset do |image_asset|
        image_asset.data = get_image_bytes(url)
      end
    end
  end

  # Create an AssetGroupAsset to link the Asset to the AssetGroup.
  operations << client.operation.mutate do |m|
    m.asset_group_asset_operation = client.operation.create_resource
        .asset_group_asset do |aga|
      aga.field_type = field_type
      aga.asset_group = client.path.asset_group(
        customer_id,
        ASSET_GROUP_TEMPORARY_ID)
      aga.asset = client.path.asset(customer_id, temp_id)
    end
  end

  operations
end

# Sorts a list of asset and asset group asset operations.  This sorts the list
# such that all asset operations precede all asset group asset operations. If
# asset group assets are created before assets then an error will be returned
# by the API.
def sort_asset_and_asset_group_asset_operations(operations)
  operations.sort_by do |operation|
    if operation.asset_group_asset_operation
      1
    else
      0
    end
  end
end

def _get_customer_conversion_goals(client, customer_id)
  query = <<~EOD
    SELECT
        customer_conversion_goal.category,
        customer_conversion_goal.origin
    FROM customer_conversion_goal
  EOD

  customer_conversion_goals = []

  ga_service = client.service.google_ads
  # The number of conversion goals is typically less than 50 so we use
  # GoogleAdsService.search instead of search_stream.
  response = ga_service.search(
      customer_id: customer_id,
      query: query,
      page_size: PAGE_SIZE,
  )

  # Iterate over the results and build the list of conversion goals.
  response.each do |row|
    customer_conversion_goals << {
        "category" => row.customer_conversion_goal.category,
        "origin" => row.customer_conversion_goal.origin
    }
  end

  customer_conversion_goals
end

def create_conversion_goal_operations(client, customer_id, customer_conversion_goals)
  campaign_conversion_goal_service = client.service.campaign_conversion_goal

  operations = []

  # To override the customer conversion goals, we will change the
  # biddability of each of the customer conversion goals so that only
  # the desired conversion goal is biddable in this campaign.
  customer_conversion_goals.each do |customer_conversion_goal|
    operations << client.operation.mutate do |m|
        m.campaign_conversion_goal_operation = client.operation.campaign_conversion_goal do |op|
          op.update = client.resource.campaign_conversion_goal do |ccg|
              ccg.resource_name = client.path.campaign_conversion_goal(
                  customer_id,
                  PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,
                  customer_conversion_goal["category"].to_s,
                  customer_conversion_goal["origin"].to_s)
              # Change the biddability for the campaign conversion goal.
              # Set biddability to True for the desired (category, origin).
              # Set biddability to False for all other conversion goals.
              # Note:
              #  1- It is assumed that this Conversion Action
              #     (category=PURCHASE, origin=WEBSITE) exists in this account.
              #  2- More than one goal can be biddable if desired. This example
              #     shows only one.
              ccg.biddable = (customer_conversion_goal["category"] == :PURCHASE &&
                  customer_conversion_goal["origin"] == :WEBSITE)
          end
          op.update_mask = Google::Ads::GoogleAds::FieldMaskUtil.all_set_fields_of(op.update)
        end
    end
  end

  operations
end

# Loads image data from a URL.
def get_image_bytes(url)
    URI.open(url).read
  end

  # Prints the details of a MutateGoogleAdsResponse.
  def print_response_details(response)
    # Parse the mutate response to print details about the entities that
    # were created by the request.
    suffix = "_result"
    response.mutate_operation_responses.each do |result|
      result.to_h.select {|k, v| v }.each do |name, value|
        if name.to_s.end_with?(suffix)
          name = name.to_s.delete_suffix(suffix)
        end

        puts "Created a(n) #{::Google::Ads::GoogleAds::Utils.camelize(name)} " \
          "with #{value.to_s.strip}."
      end
    end
  end

if __FILE__ == $0
    PAGE_SIZE = 1000

    options = {}

    # The following parameter(s) should be provided to run the example. You can
    # either specify these by changing the INSERT_XXX_ID_HERE values below, or on
    # the command line.
    #
    # Parameters passed on the command line will override any parameters set in
    # code.
    #
    # Running the example with -h will print the command line usage.
    options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'
    options[:merchant_center_account_id] = 'INSERT_MERCHANT_CENTER_ACCOUNT_ID_HERE'
    options[:final_url] = 'INSERT_FINAL_URL_HERE'

    OptionParser.new do |opts|
      opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__))

      opts.separator ''
      opts.separator 'Options:'

      opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|
        options[:customer_id] = v
      end

      opts.on('-m', '--merchant-center-account-id MERCHANT-CENTER-ACCOUNT-ID',
          Integer, 'Merchant Center Account ID') do |v|
        options[:merchant_center_account_id] = v
      end

      opts.on('-f', '--final-url FINAL-URL', String, 'Final URL') do |v|
        options[:final_url] = v
      end

      opts.separator ''
      opts.separator 'Help:'

      opts.on_tail('-h', '--help', 'Show this message') do
        puts opts
        exit
      end
    end.parse!

    begin
      add_performance_max_retail_campaign(
          options.fetch(:customer_id).tr("-", ""),
          options.fetch(:merchant_center_account_id),
          options.fetch(:final_url))
    rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e
      e.failure.errors.each do |error|
        STDERR.printf("Error with message: %s\n", error.message)
        if error.location
          error.location.field_path_elements.each do |field_path_element|
            STDERR.printf("\tOn field: %s\n", field_path_element.field_name)
          end
        end
        error.error_code.to_h.each do |k, v|
          next if v == :UNSPECIFIED
          STDERR.printf("\tType: %s\n\tCode: %s\n", k, v)
        end
      end
      raise
    end
  end

      

پرل

#!/usr/bin/perl -w
#
# Copyright 2021, Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This example shows how to create a Performance Max retail campaign.
#
# This will be created for "All products".
#
# For more information about Performance Max retail campaigns, see
# https://developers.google.com/google-ads/api/docs/performance-max/retail.
#
# Prerequisites:
# - You need to have access to a Merchant Center account. You can find
#   instructions to create a Merchant Center account here:
#   https://support.google.com/merchants/answer/188924.
#   This account must be linked to your Google Ads account. The integration
#   instructions can be found at:
#   https://developers.google.com/google-ads/api/docs/shopping-ads/merchant-center.
# - You need your Google Ads account to track conversions. The different ways
#   to track conversions can be found here:
#   https://support.google.com/google-ads/answer/1722054.
# - You must have at least one conversion action in the account. For more about
#   conversion actions, see
#   https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions.

use strict;
use warnings;
use utf8;

use FindBin qw($Bin);
use lib "$Bin/../../lib";
use Google::Ads::GoogleAds::Client;
use Google::Ads::GoogleAds::Utils::GoogleAdsHelper;
use Google::Ads::GoogleAds::Utils::MediaUtils;
use Google::Ads::GoogleAds::Utils::FieldMasks;
use Google::Ads::GoogleAds::V17::Resources::CampaignBudget;
use Google::Ads::GoogleAds::V17::Resources::Campaign;
use Google::Ads::GoogleAds::V17::Resources::ShoppingSetting;
use Google::Ads::GoogleAds::V17::Resources::CampaignCriterion;
use Google::Ads::GoogleAds::V17::Resources::Asset;
use Google::Ads::GoogleAds::V17::Resources::AssetGroup;
use Google::Ads::GoogleAds::V17::Resources::AssetGroupAsset;
use Google::Ads::GoogleAds::V17::Resources::CampaignConversionGoal;
use Google::Ads::GoogleAds::V17::Resources::AssetGroupListingGroupFilter;
use Google::Ads::GoogleAds::V17::Common::MaximizeConversionValue;
use Google::Ads::GoogleAds::V17::Common::LocationInfo;
use Google::Ads::GoogleAds::V17::Common::LanguageInfo;
use Google::Ads::GoogleAds::V17::Common::TextAsset;
use Google::Ads::GoogleAds::V17::Common::ImageAsset;
use Google::Ads::GoogleAds::V17::Enums::BudgetDeliveryMethodEnum qw(STANDARD);
use Google::Ads::GoogleAds::V17::Enums::CampaignStatusEnum;
use Google::Ads::GoogleAds::V17::Enums::AdvertisingChannelTypeEnum
  qw(PERFORMANCE_MAX);
use Google::Ads::GoogleAds::V17::Enums::AssetGroupStatusEnum;
use Google::Ads::GoogleAds::V17::Enums::AssetFieldTypeEnum
  qw(HEADLINE DESCRIPTION LONG_HEADLINE BUSINESS_NAME LOGO MARKETING_IMAGE SQUARE_MARKETING_IMAGE);
use Google::Ads::GoogleAds::V17::Enums::ConversionActionCategoryEnum
  qw(PURCHASE);
use Google::Ads::GoogleAds::V17::Enums::ConversionOriginEnum qw(WEBSITE);
use Google::Ads::GoogleAds::V17::Enums::ListingGroupFilterTypeEnum
  qw(UNIT_INCLUDED);
use Google::Ads::GoogleAds::V17::Enums::ListingGroupFilterListingSourceEnum
  qw(SHOPPING);
use Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation;
use
  Google::Ads::GoogleAds::V17::Services::CampaignBudgetService::CampaignBudgetOperation;
use Google::Ads::GoogleAds::V17::Services::CampaignService::CampaignOperation;
use
  Google::Ads::GoogleAds::V17::Services::CampaignCriterionService::CampaignCriterionOperation;
use Google::Ads::GoogleAds::V17::Services::AssetService::AssetOperation;
use
  Google::Ads::GoogleAds::V17::Services::AssetGroupService::AssetGroupOperation;
use
  Google::Ads::GoogleAds::V17::Services::AssetGroupAssetService::AssetGroupAssetOperation;
use
  Google::Ads::GoogleAds::V17::Services::CampaignConversionGoalService::CampaignConversionGoalOperation;
use
  Google::Ads::GoogleAds::V17::Services::AssetGroupListingGroupFilterService::AssetGroupListingGroupFilterOperation;
use Google::Ads::GoogleAds::V17::Utils::ResourceNames;

use Getopt::Long qw(:config auto_help);
use Pod::Usage;
use Cwd          qw(abs_path);
use Data::Uniqid qw(uniqid);
use POSIX        qw(strftime);

# We specify temporary IDs that are specific to a single mutate request.
# Temporary IDs are always negative and unique within one mutate request.
#
# See https://developers.google.com/google-ads/api/docs/mutating/best-practices
# for further details.
#
# These temporary IDs are fixed because they are used in multiple places.
use constant BUDGET_TEMPORARY_ID                   => -1;
use constant PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID => -2;
use constant ASSET_GROUP_TEMPORARY_ID              => -3;

# There are also entities that will be created in the same request but do not
# need to be fixed temporary IDs because they are referenced only once.
our $next_temp_id = ASSET_GROUP_TEMPORARY_ID - 1;

sub add_performance_max_retail_campaign {
  my ($api_client, $customer_id, $merchant_center_account_id, $final_url) = @_;

  # This campaign will override the customer conversion goals.
  # Retrieve the current list of customer conversion goals.
  my $customer_conversion_goals =
    get_customer_conversion_goals($api_client, $customer_id);

  # Performance Max campaigns require that repeated assets such as headlines
  # and descriptions be created before the campaign.
  # For the list of required assets for a Performance Max campaign, see
  # https://developers.google.com/google-ads/api/docs/performance-max/assets.
  #
  # Create the headlines.
  my $headline_asset_resource_names =
    create_multiple_text_assets($api_client, $customer_id,
    ["Travel", "Travel Reviews", "Book travel"]);
  # Create the descriptions.
  my $description_asset_resource_names =
    create_multiple_text_assets($api_client, $customer_id,
    ["Take to the air!", "Fly to the sky!"]);

  # It's important to create the below entities in this order because they depend
  # on each other.
  my $operations = [];
  # The below methods create and return MutateOperations that we later provide to
  # the GoogleAdsService->mutate() method in order to create the entities in a
  # single request. Since the entities for a Performance Max campaign are closely
  # tied to one-another, it's considered a best practice to create them in a
  # single mutate request so they all complete successfully or fail entirely,
  # leaving no orphaned entities. See:
  # https://developers.google.com/google-ads/api/docs/mutating/overview.
  push @$operations, create_campaign_budget_operation($customer_id);
  push @$operations,
    create_performance_max_campaign_operation($customer_id,
    $merchant_center_account_id);
  push @$operations, @{create_campaign_criterion_operations($customer_id)};
  push @$operations, create_asset_group_operation($customer_id, $final_url);
  push @$operations, create_listing_group_filter_operation($customer_id);
  push @$operations,
    @{
    create_asset_and_asset_group_asset_operations(
      $customer_id, $headline_asset_resource_names,
      $description_asset_resource_names
    )};
  push @$operations,
    @{create_conversion_goal_operations($customer_id,
      $customer_conversion_goals)};

  # Issue a mutate request to create everything and print its information.
  my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({
    customerId       => $customer_id,
    mutateOperations => $operations
  });

  print_response_details($mutate_google_ads_response);

  return 1;
}

# Creates a MutateOperation that creates a new CampaignBudget.
#
# A temporary ID will be assigned to this campaign budget so that it can be
# referenced by other objects being created in the same mutate request.
sub create_campaign_budget_operation {
  my ($customer_id) = @_;

  # Create a mutate operation that creates a campaign budget operation.
  return
    Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation->
    new({
      campaignBudgetOperation =>
        Google::Ads::GoogleAds::V17::Services::CampaignBudgetService::CampaignBudgetOperation
        ->new({
          create => Google::Ads::GoogleAds::V17::Resources::CampaignBudget->new(
            {
              # Set a temporary ID in the budget's resource name so it can be
              # referenced by the campaign in later steps.
              resourceName =>
                Google::Ads::GoogleAds::V17::Utils::ResourceNames::campaign_budget(
                $customer_id, BUDGET_TEMPORARY_ID
                ),
              name => "Performance Max retail campaign budget #" . uniqid(),
              # The budget period already defaults to DAILY.
              amountMicros   => 50000000,
              deliveryMethod => STANDARD,
              # A Performance Max campaign cannot use a shared campaign budget.
              explicitlyShared => "false",
            })})});
}

# Creates a MutateOperation that creates a new Performance Max campaign.
#
# A temporary ID will be assigned to this campaign so that it can be referenced
# by other objects being created in the same mutate request.
sub create_performance_max_campaign_operation {
  my ($customer_id, $merchant_center_account_id) = @_;

  # Create a mutate operation that creates a campaign operation.
  return
    Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation->
    new({
      campaignOperation =>
        Google::Ads::GoogleAds::V17::Services::CampaignService::CampaignOperation
        ->new({
          create => Google::Ads::GoogleAds::V17::Resources::Campaign->new({
              # Assign the resource name with a temporary ID.
              resourceName =>
                Google::Ads::GoogleAds::V17::Utils::ResourceNames::campaign(
                $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                ),
              name => "Performance Max retail campaign #'" . uniqid(),
              # Set the budget using the given budget resource name.
              campaignBudget =>
                Google::Ads::GoogleAds::V17::Utils::ResourceNames::campaign_budget(
                $customer_id, BUDGET_TEMPORARY_ID
                ),
              # Set the campaign status as PAUSED. The campaign is the only entity in
              # the mutate request that should have its status set.
              status =>
                Google::Ads::GoogleAds::V17::Enums::CampaignStatusEnum::PAUSED,
              # All Performance Max campaigns have an advertisingChannelType of
              # PERFORMANCE_MAX. The advertisingChannelSubType should not be set.
              advertisingChannelType => PERFORMANCE_MAX,

              # Bidding strategy must be set directly on the campaign.
              # Setting a portfolio bidding strategy by resource name is not supported.
              # Max Conversion and Max Conversion Value are the only strategies
              # supported for Performance Max campaigns.
              # An optional ROAS (Return on Advertising Spend) can be set for
              # maximizeConversionValue. The ROAS value must be specified as a ratio in
              # the API. It is calculated by dividing "total value" by "total spend".
              # For more information on Max Conversion Value, see the support article:
              # http://support.google.com/google-ads/answer/7684216.
              # A targetRoas of 3.5 corresponds to a 350% return on ad spend.
              # For first time users, it's recommended not to set a target ROAS.
              # maximizeConversionValue =>
              #   Google::Ads::GoogleAds::V17::Common::MaximizeConversionValue->
              #   new({
              #     targetRoas => 3.5
              #   }
              # ),
              # Below is what you would use if you want to maximize conversions.
              # maximizeConversions =>
              #   Google::Ads::GoogleAds::V17::Common::MaximizeConversions->
              #   new({
              #     targetCpaMicros => 1000000
              #   }
              # ),
              # The target CPA is optional. This is the average amount that you would
              # like to spend per conversion action.

              # Set the shopping settings.
              shoppingSetting =>
                Google::Ads::GoogleAds::V17::Resources::ShoppingSetting->new({
                  merchantId => $merchant_center_account_id,
                  # Optional: To use products only from a specific feed, set feedLabel
                  # to the feed label used in Merchant Center.
                  # See: https://support.google.com/merchants/answer/12453549.
                  # Omitting the feedLabel field will use products from all feeds.
                  # feedLabel => "INSERT_FEED_LABEL_HERE"
                }
                ),

              # Set the final URL expansion opt out. This flag is specific to
              # Performance Max campaigns. If opted out (true), only the final URLs in
              # the asset group or URLs specified in the advertiser's Google Merchant
              # Center or business data feeds are targeted.
              # If opted in (false), the entire domain will be targeted. For best
              # results, set this value to false to opt in and allow URL expansions. You
              # can optionally add exclusions to limit traffic to parts of your website.
              #
              # For a Retail campaign, we want the final URL to be limited to those
              # explicitly surfaced via GMC.
              urlExpansionOptOut => "true",

              # Optional fields.
              startDate => strftime("%Y%m%d", localtime(time + 60 * 60 * 24)),
              endDate   =>
                strftime("%Y%m%d", localtime(time + 60 * 60 * 24 * 365)),
            })})});
}

# Creates a list of MutateOperations that create new campaign criteria.
sub create_campaign_criterion_operations {
  my ($customer_id) = @_;

  my $operations = [];
  # Set the LOCATION campaign criteria.
  # Target all of New York City except Brooklyn.
  # Location IDs are listed here:
  # https://developers.google.com/google-ads/api/reference/data/geotargets
  # and they can also be retrieved using the GeoTargetConstantService as shown
  # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting.
  push @$operations,
    Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation->
    new({
      campaignCriterionOperation =>
        Google::Ads::GoogleAds::V17::Services::CampaignCriterionService::CampaignCriterionOperation
        ->new({
          create =>
            Google::Ads::GoogleAds::V17::Resources::CampaignCriterion->new({
              campaign =>
                Google::Ads::GoogleAds::V17::Utils::ResourceNames::campaign(
                $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                ),
              # Adds one positive location target for New York City (ID=1023191),
              # specifically adding the positive criteria before the negative one.
              location =>
                Google::Ads::GoogleAds::V17::Common::LocationInfo->new({
                  geoTargetConstant =>
                    Google::Ads::GoogleAds::V17::Utils::ResourceNames::geo_target_constant(
                    1023191)}
                ),
              negative => "false"
            })})});

  # Next add the negative target for Brooklyn (ID=1022762).
  push @$operations,
    Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation->
    new({
      campaignCriterionOperation =>
        Google::Ads::GoogleAds::V17::Services::CampaignCriterionService::CampaignCriterionOperation
        ->new({
          create =>
            Google::Ads::GoogleAds::V17::Resources::CampaignCriterion->new({
              campaign =>
                Google::Ads::GoogleAds::V17::Utils::ResourceNames::campaign(
                $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                ),
              location =>
                Google::Ads::GoogleAds::V17::Common::LocationInfo->new({
                  geoTargetConstant =>
                    Google::Ads::GoogleAds::V17::Utils::ResourceNames::geo_target_constant(
                    1022762)}
                ),
              negative => "true"
            })})});

  # Set the LANGUAGE campaign criterion.
  push @$operations,
    Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation->
    new({
      campaignCriterionOperation =>
        Google::Ads::GoogleAds::V17::Services::CampaignCriterionService::CampaignCriterionOperation
        ->new({
          create =>
            Google::Ads::GoogleAds::V17::Resources::CampaignCriterion->new({
              campaign =>
                Google::Ads::GoogleAds::V17::Utils::ResourceNames::campaign(
                $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                ),
              # Set the language.
              # For a list of all language codes, see:
              # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7.
              language =>
                Google::Ads::GoogleAds::V17::Common::LanguageInfo->new({
                  languageConstant =>
                    Google::Ads::GoogleAds::V17::Utils::ResourceNames::language_constant(
                    1000)    # English
                })})})});

  return $operations;
}

# Creates multiple text assets and returns the list of resource names.
#
# These repeated assets must be created in a separate request prior to
# creating the campaign.
sub create_multiple_text_assets {
  my ($api_client, $customer_id, $texts) = @_;

  # Here again we use the GoogleAdService to create multiple text assets in a
  # single request.
  my $operations = [];
  foreach my $text (@$texts) {
    # Create a mutate operation for a text asset.
    push @$operations,
      Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation
      ->new({
        assetOperation =>
          Google::Ads::GoogleAds::V17::Services::AssetService::AssetOperation->
          new({
            create => Google::Ads::GoogleAds::V17::Resources::Asset->new({
                textAsset =>
                  Google::Ads::GoogleAds::V17::Common::TextAsset->new({
                    text => $text
                  })})})});
  }

  # Issue a mutate request to add all assets.
  my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({
    customerId       => $customer_id,
    mutateOperations => $operations
  });

  my $asset_resource_names = [];
  foreach
    my $response (@{$mutate_google_ads_response->{mutateOperationResponses}})
  {
    push @$asset_resource_names, $response->{assetResult}{resourceName};
  }
  print_response_details($mutate_google_ads_response);

  return $asset_resource_names;
}

# Creates a MutateOperation that creates a new asset group.
#
# A temporary ID will be assigned to this asset group so that it can be referenced
# by other objects being created in the same mutate request.
sub create_asset_group_operation {
  my ($customer_id, $final_url) = @_;

  # Create a mutate operation that creates an asset group operation.
  return
    Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation->
    new({
      assetGroupOperation =>
        Google::Ads::GoogleAds::V17::Services::AssetGroupService::AssetGroupOperation
        ->new({
          create => Google::Ads::GoogleAds::V17::Resources::AssetGroup->new({
              resourceName =>
                Google::Ads::GoogleAds::V17::Utils::ResourceNames::asset_group(
                $customer_id, ASSET_GROUP_TEMPORARY_ID
                ),
              name     => "Performance Max retail asset group #" . uniqid(),
              campaign =>
                Google::Ads::GoogleAds::V17::Utils::ResourceNames::campaign(
                $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                ),
              finalUrls       => [$final_url],
              finalMobileUrls => [$final_url],
              status          =>
                Google::Ads::GoogleAds::V17::Enums::AssetGroupStatusEnum::PAUSED
            })})});
}

# Creates a MutateOperation that creates a new listing group filter.
# A temporary ID will be assigned to this listing group filter so that it
# can be referenced by other objects being created in the same Mutate request.
sub create_listing_group_filter_operation {
  my ($customer_id) = @_;

  # Creates a new ad group criterion containing the "default" listing group
  # (All products).
  my $listing_group_filter =
    Google::Ads::GoogleAds::V17::Resources::AssetGroupListingGroupFilter->new({
      assetGroup =>
        Google::Ads::GoogleAds::V17::Utils::ResourceNames::asset_group(
        $customer_id, ASSET_GROUP_TEMPORARY_ID
        ),

      # Since this is the root node, do not set the parentListingGroupFilter.
      # For all other nodes, this would refer to the parent listing group filter
      # resource name.
      # parentListingGroupFilter => "<PARENT FILTER NAME>"

      # The subdivision type means this node has children. This type is used for
      # the root node as well.
      type => UNIT_INCLUDED,

      # Because this is a Performance Max campaign for retail, we need to specify
      # that this is in the shopping listing source.
      listingSource => SHOPPING
    });

  return
    Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation->
    new({
      assetGroupListingGroupFilterOperation =>
        Google::Ads::GoogleAds::V17::Services::AssetGroupListingGroupFilterService::AssetGroupListingGroupFilterOperation
        ->new({
          create => $listing_group_filter
        })});
}

# Creates a list of MutateOperations that create a new asset_group.
# A temporary ID will be assigned to this asset group so that it can
# be referenced by other objects being created in the same Mutate request.
sub create_asset_and_asset_group_asset_operations {
  my (
    $customer_id,
    $headline_asset_resource_names,
    $description_asset_resource_names
  ) = @_;

  my $operations = [];
  # For the list of required assets for a Performance Max campaign, see
  # https://developers.google.com/google-ads/api/docs/performance-max/assets.

  # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset
  # and providing:
  # - the resource name of the AssetGroup
  # - the resource name of the Asset
  # - the fieldType of the Asset in this AssetGroup
  #
  # To learn more about AssetGroups, see
  # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups.

  # Link the previously created multiple text assets.

  # Link the headline assets.
  foreach my $resource_name (@$headline_asset_resource_names) {
    push @$operations,
      Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation
      ->new({
        assetGroupAssetOperation =>
          Google::Ads::GoogleAds::V17::Services::AssetGroupAssetService::AssetGroupAssetOperation
          ->new({
            create =>
              Google::Ads::GoogleAds::V17::Resources::AssetGroupAsset->new({
                asset      => $resource_name,
                assetGroup =>
                  Google::Ads::GoogleAds::V17::Utils::ResourceNames::asset_group(
                  $customer_id, ASSET_GROUP_TEMPORARY_ID
                  ),
                fieldType => HEADLINE
              })})});
  }

  # Link the description assets.
  foreach my $resource_name (@$description_asset_resource_names) {
    push @$operations,
      Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation
      ->new({
        assetGroupAssetOperation =>
          Google::Ads::GoogleAds::V17::Services::AssetGroupAssetService::AssetGroupAssetOperation
          ->new({
            create =>
              Google::Ads::GoogleAds::V17::Resources::AssetGroupAsset->new({
                asset      => $resource_name,
                assetGroup =>
                  Google::Ads::GoogleAds::V17::Utils::ResourceNames::asset_group(
                  $customer_id, ASSET_GROUP_TEMPORARY_ID
                  ),
                fieldType => DESCRIPTION
              })})});
  }

  # Create and link the long headline text asset.
  push @$operations,
    @{create_and_link_text_asset($customer_id, "Travel the World",
      LONG_HEADLINE)};

  # Create and link the business name text asset.
  push @$operations,
    @{
    create_and_link_text_asset($customer_id, "Interplanetary Cruises",
      BUSINESS_NAME)};

  # Create and link the image assets.

  # Create and link the logo asset.
  push @$operations,
    @{
    create_and_link_image_asset($customer_id, "https://gaagl.page.link/1Crm",
      LOGO, "Logo Image")};

  # Create and link the marketing image asset.
  push @$operations,
    @{
    create_and_link_image_asset(
      $customer_id,    "https://gaagl.page.link/Eit5",
      MARKETING_IMAGE, "Marketing Image"
    )};

  # Create and link the square marketing image asset.
  push @$operations,
    @{
    create_and_link_image_asset(
      $customer_id,           "https://gaagl.page.link/bjYi",
      SQUARE_MARKETING_IMAGE, "Square Marketing Image"
    )};

  # After being created the list must be sorted so that all asset
  # operations come before all the asset group asset operations,
  # otherwise the API will reject the request.
  return sort_asset_and_asset_group_asset_operations($operations);
}

# Creates a list of MutateOperations that create a new linked text asset.
sub create_and_link_text_asset {
  my ($customer_id, $text, $field_type) = @_;

  my $operations = [];
  # Create a new mutate operation for a text asset.
  push @$operations,
    Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation->
    new({
      assetOperation =>
        Google::Ads::GoogleAds::V17::Services::AssetService::AssetOperation->
        new({
          create => Google::Ads::GoogleAds::V17::Resources::Asset->new({
              resourceName =>
                Google::Ads::GoogleAds::V17::Utils::ResourceNames::asset(
                $customer_id, $next_temp_id
                ),
              textAsset => Google::Ads::GoogleAds::V17::Common::TextAsset->new({
                  text => $text
                })})})});

  # Create an asset group asset to link the asset to the asset group.
  push @$operations,
    Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation->
    new({
      assetGroupAssetOperation =>
        Google::Ads::GoogleAds::V17::Services::AssetGroupAssetService::AssetGroupAssetOperation
        ->new({
          create =>
            Google::Ads::GoogleAds::V17::Resources::AssetGroupAsset->new({
              asset => Google::Ads::GoogleAds::V17::Utils::ResourceNames::asset(
                $customer_id, $next_temp_id
              ),
              assetGroup =>
                Google::Ads::GoogleAds::V17::Utils::ResourceNames::asset_group(
                $customer_id, ASSET_GROUP_TEMPORARY_ID
                ),
              fieldType => $field_type
            })})});

  $next_temp_id--;
  return $operations;
}

# Creates a list of MutateOperations that create a new linked image asset.
sub create_and_link_image_asset {
  my ($customer_id, $url, $field_type, $asset_name) = @_;

  my $operations = [];
  # Create a new mutate operation for an image asset.
  push @$operations,
    Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation->
    new({
      assetOperation =>
        Google::Ads::GoogleAds::V17::Services::AssetService::AssetOperation->
        new({
          create => Google::Ads::GoogleAds::V17::Resources::Asset->new({
              resourceName =>
                Google::Ads::GoogleAds::V17::Utils::ResourceNames::asset(
                $customer_id, $next_temp_id
                ),
              # Provide a unique friendly name to identify your asset.
              # When there is an existing image asset with the same content but a different
              # name, the new name will be dropped silently.
              name       => $asset_name,
              imageAsset =>
                Google::Ads::GoogleAds::V17::Common::ImageAsset->new({
                  data => get_base64_data_from_url($url)})})})});

  # Create an asset group asset to link the asset to the asset group.
  push @$operations,
    Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation->
    new({
      assetGroupAssetOperation =>
        Google::Ads::GoogleAds::V17::Services::AssetGroupAssetService::AssetGroupAssetOperation
        ->new({
          create =>
            Google::Ads::GoogleAds::V17::Resources::AssetGroupAsset->new({
              asset => Google::Ads::GoogleAds::V17::Utils::ResourceNames::asset(
                $customer_id, $next_temp_id
              ),
              assetGroup =>
                Google::Ads::GoogleAds::V17::Utils::ResourceNames::asset_group(
                $customer_id, ASSET_GROUP_TEMPORARY_ID
                ),
              fieldType => $field_type
            })})});

  $next_temp_id--;
  return $operations;
}

# Sorts a list of asset and asset group asset operations.
#
# This sorts the list such that all asset operations precede
# all asset group asset operations. If asset group assets are
# created before assets then an error will be returned by the API.
sub sort_asset_and_asset_group_asset_operations {
  my ($operations) = @_;

  sub sorter {
    if (defined $a->{assetOperation}) {
      return -1;
    } else {
      return 1;
    }
  }
  my @operations_sorted = sort sorter @$operations;
  return \@operations_sorted;
}

# Retrieves the list of customer conversion goals.
sub get_customer_conversion_goals {
  my ($api_client, $customer_id) = @_;

  my $customer_conversion_goals = [];
  # Create a query that retrieves all customer conversion goals.
  my $query =
    "SELECT customer_conversion_goal.category, customer_conversion_goal.origin "
    . "FROM customer_conversion_goal";
  # The number of conversion goals is typically less than 50 so we use
  # GoogleAdsService->search() method instead of search_stream().
  my $search_response = $api_client->GoogleAdsService()->search({
    customerId => $customer_id,
    query      => $query
  });

  # Iterate over the results and build the list of conversion goals.
  foreach my $google_ads_row (@{$search_response->{results}}) {
    push @$customer_conversion_goals,
      {
      category => $google_ads_row->{customerConversionGoal}{category},
      origin   => $google_ads_row->{customerConversionGoal}{origin}};
  }

  return $customer_conversion_goals;
}

# Creates a list of MutateOperations that override customer conversion goals.
sub create_conversion_goal_operations {
  my ($customer_id, $customer_conversion_goals) = @_;

  my $operations = [];
  # To override the customer conversion goals, we will change the biddability of
  # each of the customer conversion goals so that only the desired conversion goal
  # is biddable in this campaign.
  foreach my $customer_conversion_goal (@$customer_conversion_goals) {
    my $campaign_conversion_goal =
      Google::Ads::GoogleAds::V17::Resources::CampaignConversionGoal->new({
        resourceName =>
          Google::Ads::GoogleAds::V17::Utils::ResourceNames::campaign_conversion_goal(
          $customer_id,
          PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,
          $customer_conversion_goal->{category},
          $customer_conversion_goal->{origin})});
    # Change the biddability for the campaign conversion goal.
    # Set biddability to true for the desired (category, origin).
    # Set biddability to false for all other conversion goals.
    # Note:
    #  1- It is assumed that this Conversion Action
    #     (category=PURCHASE, origin=WEBSITE) exists in this account.
    #  2- More than one goal can be biddable if desired. This example
    #     shows only one.
    if ( $customer_conversion_goal->{category} eq PURCHASE
      && $customer_conversion_goal->{origin} eq WEBSITE)
    {
      $campaign_conversion_goal->{biddable} = "true";
    } else {
      $campaign_conversion_goal->{biddable} = "false";
    }

    push @$operations,
      Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation
      ->new({
        campaignConversionGoalOperation =>
          Google::Ads::GoogleAds::V17::Services::CampaignConversionGoalService::CampaignConversionGoalOperation
          ->new({
            update => $campaign_conversion_goal,
            # Set the update mask on the operation. Here the update mask will be
            # a list of all the fields that were set on the update object.
            updateMask => all_set_fields_of($campaign_conversion_goal)})});
  }

  return $operations;
}

# Prints the details of a MutateGoogleAdsResponse.
# Parses the "response" oneof field name and uses it to extract the new entity's
# name and resource name.
sub print_response_details {
  my ($mutate_google_ads_response) = @_;

  foreach
    my $response (@{$mutate_google_ads_response->{mutateOperationResponses}})
  {
    my $result_type = [keys %$response]->[0];

    printf "Created a(n) %s with '%s'.\n",
      ucfirst $result_type =~ s/Result$//r,
      $response->{$result_type}{resourceName};
  }
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Get Google Ads Client, credentials will be read from ~/googleads.properties.
my $api_client = Google::Ads::GoogleAds::Client->new();

# By default examples are set to die on any server returned fault.
$api_client->set_die_on_faults(1);

my $customer_id                = undef;
my $merchant_center_account_id = undef;
my $sales_country              = "US";
my $final_url                  = "http://www.example.com";

# Parameters passed on the command line will override any parameters set in code.
GetOptions(
  "customer_id=s"                => \$customer_id,
  "merchant_center_account_id=i" => \$merchant_center_account_id,
  "sales_country=s"              => \$sales_country,
  "final_url=s"                  => \$final_url
);

# Print the help message if the parameters are not initialized in the code nor
# in the command line.
pod2usage(2)
  if not check_params($customer_id, $merchant_center_account_id, $sales_country,
  $final_url);

# Call the example.
add_performance_max_retail_campaign($api_client, $customer_id =~ s/-//gr,
  $merchant_center_account_id, $sales_country, $final_url);

=pod

=head1 NAME

add_performance_max_retail_campaign

=head1 DESCRIPTION

This example shows how to create a Performance Max retail campaign.

This will be created for "All products".

For more information about Performance Max retail campaigns, see
https://developers.google.com/google-ads/api/docs/performance-max/retail.

Prerequisites:
- You need to have access to a Merchant Center account. You can find
  instructions to create a Merchant Center account here:
  https://support.google.com/merchants/answer/188924.
  This account must be linked to your Google Ads account. The integration
  instructions can be found at:
  https://developers.google.com/google-ads/api/docs/shopping-ads/merchant-center.
- You need your Google Ads account to track conversions. The different ways
  to track conversions can be found here:
  https://support.google.com/google-ads/answer/1722054.
- You must have at least one conversion action in the account. For more about
  conversion actions, see
  https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions.

=head1 SYNOPSIS

add_performance_max_retail_campaign.pl [options]

    -help                         Show the help message.
    -customer_id                  The Google Ads customer ID.
    -merchant_center_account_id   The Merchant Center account ID.
    -sales_country                [optional] The sales country of products to include in the campaign.
    -final_url                    [optional] The final URL for the asset group of the campaign.

=cut