Add project files.
This commit is contained in:
576
GoogleApi/Services/GoogleAdsService.cs
Normal file
576
GoogleApi/Services/GoogleAdsService.cs
Normal file
@@ -0,0 +1,576 @@
|
||||
using GoogleApi.Configuration;
|
||||
using GoogleApi.Models;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
using Google.Ads.GoogleAds;
|
||||
using Google.Ads.GoogleAds.Lib;
|
||||
|
||||
using Google.Ads.GoogleAds.V22.Common;
|
||||
using Google.Ads.GoogleAds.V22.Enums;
|
||||
using Google.Ads.GoogleAds.V22.Errors;
|
||||
using Google.Ads.GoogleAds.V22.Resources;
|
||||
using Google.Ads.GoogleAds.V22.Services;
|
||||
|
||||
namespace GoogleApi.Services;
|
||||
|
||||
// ✅ IMPORTANT: force "Services" to mean Google.Ads.GoogleAds.Services (not GoogleApi.Services)
|
||||
using GAdsServices = global::Google.Ads.GoogleAds.Services;
|
||||
|
||||
// ✅ Avoid name collision with Google.Ads.GoogleAds.V22.Resources.BiddingStrategy
|
||||
using ModelBiddingStrategy = GoogleApi.Models.BiddingStrategy;
|
||||
|
||||
public sealed class GoogleAdsService
|
||||
{
|
||||
private readonly GoogleAdsConfig _config;
|
||||
private readonly GoogleAdsClientFactory _clientFactory;
|
||||
private readonly ILogger<GoogleAdsService> _logger;
|
||||
|
||||
public GoogleAdsService(
|
||||
IOptions<GoogleAdsConfig> config,
|
||||
GoogleAdsClientFactory clientFactory,
|
||||
ILogger<GoogleAdsService> logger)
|
||||
{
|
||||
_config = config.Value;
|
||||
_clientFactory = clientFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<ProviderResponse> ExecuteAsync(ProviderRequest request, CancellationToken ct)
|
||||
{
|
||||
var requestId = request.RequestId ?? Guid.NewGuid().ToString("N");
|
||||
var operation = (request.Operation ?? string.Empty).Trim();
|
||||
|
||||
_logger.LogInformation(
|
||||
"[GoogleAds] Executing {Operation} | RequestId={RequestId} TenantId={TenantId} RealApi={RealApi}",
|
||||
operation, requestId, request.TenantId, _clientFactory.IsRealApiEnabled);
|
||||
|
||||
try
|
||||
{
|
||||
var context = new GoogleAdsContext
|
||||
{
|
||||
CustomerId = GoogleAdsClientFactory.NormalizeCustomerId(request.TenantId ?? string.Empty),
|
||||
LoginCustomerId = request.LoginCustomerId
|
||||
};
|
||||
|
||||
var result = operation switch
|
||||
{
|
||||
"Ping" => Ping(requestId),
|
||||
"TestPing" => Ping(requestId),
|
||||
|
||||
"CreateCampaign" => await CreateCampaignAsync(request, context, requestId, ct),
|
||||
"GetCampaign" => await GetCampaignAsync(request, context, requestId, ct),
|
||||
"UpdateCampaign" => await UpdateCampaignAsync(request, context, requestId, ct),
|
||||
"ListCampaigns" => await ListCampaignsAsync(request, context, requestId, ct),
|
||||
|
||||
"GetCampaignStats" => GetCampaignStats(request, requestId),
|
||||
"GetAccountStats" => GetAccountStats(request, requestId),
|
||||
|
||||
"ListAccessibleCustomers" => await ListAccessibleCustomersAsync(context, requestId, ct),
|
||||
|
||||
"" => ProviderResponse.Fail(requestId, "VALIDATION", "Operation is required"),
|
||||
_ => ProviderResponse.Fail(requestId, "UNKNOWN_OPERATION", $"Unknown operation: {operation}")
|
||||
};
|
||||
|
||||
_logger.LogInformation(
|
||||
"[GoogleAds] Completed {Operation} | RequestId={RequestId} Ok={Ok}",
|
||||
operation, requestId, result.Ok);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "[GoogleAds] Error in {Operation} | RequestId={RequestId}", operation, requestId);
|
||||
return ProviderResponse.Fail(requestId, "INTERNAL_ERROR", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private ProviderResponse Ping(string requestId)
|
||||
=> ProviderResponse.Success(requestId, new
|
||||
{
|
||||
message = "GoogleApi provider is healthy",
|
||||
service = "GoogleApi",
|
||||
realApiEnabled = _clientFactory.IsRealApiEnabled,
|
||||
apiVersion = _config.ApiVersion,
|
||||
timestamp = DateTimeOffset.UtcNow
|
||||
});
|
||||
|
||||
private async Task<ProviderResponse> CreateCampaignAsync(
|
||||
ProviderRequest request, GoogleAdsContext context, string requestId, CancellationToken ct)
|
||||
{
|
||||
var payload = request.GetPayload<CreateCampaignPayload>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(payload.Name))
|
||||
return ProviderResponse.Fail(requestId, "VALIDATION", "Campaign name is required");
|
||||
|
||||
if (payload.BudgetMicros <= 0)
|
||||
return ProviderResponse.Fail(requestId, "VALIDATION", "BudgetMicros must be > 0");
|
||||
|
||||
if (_clientFactory.IsRealApiEnabled && !string.IsNullOrWhiteSpace(context.CustomerId))
|
||||
return await CreateCampaignRealAsync(payload, context, requestId, ct);
|
||||
|
||||
var externalId = $"customers/{context.CustomerId}/campaigns/{GenerateId()}";
|
||||
_logger.LogInformation("[GoogleAds] EMULATED: Created campaign {CampaignName} => {CampaignId}", payload.Name, externalId);
|
||||
|
||||
return ProviderResponse.Success(requestId, new
|
||||
{
|
||||
externalId,
|
||||
name = payload.Name,
|
||||
type = payload.Type.ToString(),
|
||||
status = "ENABLED",
|
||||
budgetMicros = payload.BudgetMicros,
|
||||
biddingStrategy = payload.BiddingStrategy.ToString(),
|
||||
createdAt = DateTimeOffset.UtcNow,
|
||||
emulated = true
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<ProviderResponse> CreateCampaignRealAsync(
|
||||
CreateCampaignPayload payload, GoogleAdsContext context, string requestId, CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
GoogleAdsClient client = _clientFactory.CreateClient(context);
|
||||
|
||||
// 1) Budget
|
||||
CampaignBudgetServiceClient budgetService =
|
||||
client.GetService(GAdsServices.V22.CampaignBudgetService);
|
||||
|
||||
var budget = new CampaignBudget
|
||||
{
|
||||
Name = $"{payload.Name} Budget ({DateTime.UtcNow:yyyyMMddHHmmss})",
|
||||
AmountMicros = payload.BudgetMicros,
|
||||
DeliveryMethod = BudgetDeliveryMethodEnum.Types.BudgetDeliveryMethod.Standard,
|
||||
ExplicitlyShared = false
|
||||
};
|
||||
|
||||
var budgetResponse = await budgetService.MutateCampaignBudgetsAsync(
|
||||
new MutateCampaignBudgetsRequest
|
||||
{
|
||||
CustomerId = context.CustomerId,
|
||||
Operations = { new CampaignBudgetOperation { Create = budget } }
|
||||
},
|
||||
cancellationToken: ct);
|
||||
|
||||
var budgetResourceName = budgetResponse.Results.FirstOrDefault()?.ResourceName;
|
||||
if (string.IsNullOrWhiteSpace(budgetResourceName))
|
||||
return ProviderResponse.Fail(requestId, "API_ERROR", "Budget create returned no resource name");
|
||||
|
||||
// 2) Campaign
|
||||
CampaignServiceClient campaignService =
|
||||
client.GetService(GAdsServices.V22.CampaignService);
|
||||
|
||||
var campaign = new Campaign
|
||||
{
|
||||
Name = payload.Name,
|
||||
Status = CampaignStatusEnum.Types.CampaignStatus.Enabled,
|
||||
AdvertisingChannelType = MapChannelType(payload.Type),
|
||||
CampaignBudget = budgetResourceName
|
||||
};
|
||||
|
||||
// Dates must be yyyyMMdd for Google Ads API
|
||||
if (!string.IsNullOrWhiteSpace(payload.StartDate)) campaign.StartDate = payload.StartDate;
|
||||
if (!string.IsNullOrWhiteSpace(payload.EndDate)) campaign.EndDate = payload.EndDate;
|
||||
|
||||
// ✅ Apply bidding in a way that does NOT rely on Campaign.MaximizeClicks property existing
|
||||
ApplyBiddingStrategySafe(campaign, payload.BiddingStrategy);
|
||||
|
||||
var campResponse = await campaignService.MutateCampaignsAsync(
|
||||
new MutateCampaignsRequest
|
||||
{
|
||||
CustomerId = context.CustomerId,
|
||||
Operations = { new CampaignOperation { Create = campaign } }
|
||||
},
|
||||
cancellationToken: ct);
|
||||
|
||||
var campaignResourceName = campResponse.Results.FirstOrDefault()?.ResourceName;
|
||||
|
||||
return ProviderResponse.Success(requestId, new
|
||||
{
|
||||
campaignResourceName,
|
||||
budgetResourceName,
|
||||
name = payload.Name,
|
||||
type = payload.Type.ToString(),
|
||||
status = "ENABLED",
|
||||
budgetMicros = payload.BudgetMicros,
|
||||
biddingStrategy = payload.BiddingStrategy.ToString(),
|
||||
emulated = false
|
||||
});
|
||||
}
|
||||
catch (GoogleAdsException gex)
|
||||
{
|
||||
_logger.LogError(gex, "Google Ads API error creating campaign | RequestId={RequestId}", requestId);
|
||||
return HandleGoogleAdsException(gex, requestId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to create campaign via real API");
|
||||
return ProviderResponse.Fail(requestId, "API_ERROR", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<ProviderResponse> GetCampaignAsync(
|
||||
ProviderRequest request, GoogleAdsContext context, string requestId, CancellationToken ct)
|
||||
{
|
||||
var payload = request.GetPayload<GetCampaignPayload>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(payload.CampaignId))
|
||||
return ProviderResponse.Fail(requestId, "VALIDATION", "CampaignId is required");
|
||||
|
||||
if (_clientFactory.IsRealApiEnabled && !string.IsNullOrWhiteSpace(context.CustomerId))
|
||||
return await GetCampaignRealAsync(payload.CampaignId, context, requestId, ct);
|
||||
|
||||
_logger.LogInformation("[GoogleAds] EMULATED: Retrieved campaign {CampaignId}", payload.CampaignId);
|
||||
|
||||
return ProviderResponse.Success(requestId, new
|
||||
{
|
||||
externalId = payload.CampaignId,
|
||||
name = "Sample Campaign",
|
||||
type = CampaignType.Search.ToString(),
|
||||
status = "ENABLED",
|
||||
budgetMicros = 10_000_000L,
|
||||
// NOTE: GetCampaignPayload doesn't have BiddingStrategy — so don't reference it
|
||||
biddingStrategy = ModelBiddingStrategy.MaximizeClicks.ToString(),
|
||||
createdAt = DateTimeOffset.UtcNow.AddDays(-7),
|
||||
emulated = true
|
||||
});
|
||||
}
|
||||
|
||||
private Task<ProviderResponse> GetCampaignRealAsync(
|
||||
string campaignId, GoogleAdsContext context, string requestId, CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
GoogleAdsClient client = _clientFactory.CreateClient(context);
|
||||
|
||||
GoogleAdsServiceClient googleAdsService =
|
||||
client.GetService(GAdsServices.V22.GoogleAdsService);
|
||||
|
||||
var isResourceName = campaignId.Contains("/campaigns/", StringComparison.OrdinalIgnoreCase);
|
||||
var where = isResourceName
|
||||
? $"campaign.resource_name = '{campaignId}'"
|
||||
: $"campaign.id = {campaignId}";
|
||||
|
||||
var query = $@"
|
||||
SELECT
|
||||
campaign.resource_name,
|
||||
campaign.id,
|
||||
campaign.name,
|
||||
campaign.status,
|
||||
campaign.advertising_channel_type,
|
||||
campaign_budget.amount_micros
|
||||
FROM campaign
|
||||
WHERE {where}
|
||||
LIMIT 1";
|
||||
|
||||
var resp = googleAdsService.Search(new SearchGoogleAdsRequest
|
||||
{
|
||||
CustomerId = context.CustomerId,
|
||||
Query = query
|
||||
});
|
||||
|
||||
var row = resp.FirstOrDefault();
|
||||
if (row == null)
|
||||
return Task.FromResult(ProviderResponse.Fail(requestId, "NOT_FOUND", "Campaign not found"));
|
||||
|
||||
return Task.FromResult(ProviderResponse.Success(requestId, new
|
||||
{
|
||||
campaign = new
|
||||
{
|
||||
resourceName = row.Campaign.ResourceName,
|
||||
id = row.Campaign.Id,
|
||||
name = row.Campaign.Name,
|
||||
status = row.Campaign.Status.ToString(),
|
||||
channelType = row.Campaign.AdvertisingChannelType.ToString(),
|
||||
budgetMicros = row.CampaignBudget?.AmountMicros
|
||||
},
|
||||
emulated = false
|
||||
}));
|
||||
}
|
||||
catch (GoogleAdsException gex)
|
||||
{
|
||||
_logger.LogError(gex, "Google Ads API error getting campaign | RequestId={RequestId}", requestId);
|
||||
return Task.FromResult(HandleGoogleAdsException(gex, requestId));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to get campaign via real API");
|
||||
return Task.FromResult(ProviderResponse.Fail(requestId, "API_ERROR", ex.Message));
|
||||
}
|
||||
}
|
||||
|
||||
private Task<ProviderResponse> UpdateCampaignAsync(
|
||||
ProviderRequest request, GoogleAdsContext context, string requestId, CancellationToken ct)
|
||||
{
|
||||
var payload = request.GetPayload<UpdateCampaignPayload>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(payload.CampaignId))
|
||||
return Task.FromResult(ProviderResponse.Fail(requestId, "VALIDATION", "CampaignId is required"));
|
||||
|
||||
_logger.LogInformation("[GoogleAds] EMULATED: Updated campaign {CampaignId}", payload.CampaignId);
|
||||
|
||||
return Task.FromResult(ProviderResponse.Success(requestId, new
|
||||
{
|
||||
updated = true,
|
||||
campaignId = payload.CampaignId,
|
||||
updatedAt = DateTimeOffset.UtcNow,
|
||||
emulated = true
|
||||
}));
|
||||
}
|
||||
|
||||
private async Task<ProviderResponse> ListCampaignsAsync(
|
||||
ProviderRequest request, GoogleAdsContext context, string requestId, CancellationToken ct)
|
||||
{
|
||||
if (_clientFactory.IsRealApiEnabled && !string.IsNullOrWhiteSpace(context.CustomerId))
|
||||
return await ListCampaignsRealAsync(context, requestId, ct);
|
||||
|
||||
_logger.LogInformation("[GoogleAds] EMULATED: Listed campaigns for tenant {TenantId}", request.TenantId);
|
||||
|
||||
var campaigns = new[]
|
||||
{
|
||||
new { id = $"customers/{context.CustomerId}/campaigns/{GenerateId()}", name = "Brand Campaign", status = "Enabled", budgetMicros = 5_000_000L },
|
||||
new { id = $"customers/{context.CustomerId}/campaigns/{GenerateId()}", name = "Product Campaign", status = "Enabled", budgetMicros = 10_000_000L },
|
||||
new { id = $"customers/{context.CustomerId}/campaigns/{GenerateId()}", name = "Retargeting", status = "Paused", budgetMicros = 3_000_000L }
|
||||
};
|
||||
|
||||
return ProviderResponse.Success(requestId, new
|
||||
{
|
||||
campaigns,
|
||||
totalCount = campaigns.Length,
|
||||
emulated = true
|
||||
});
|
||||
}
|
||||
|
||||
private Task<ProviderResponse> ListCampaignsRealAsync(
|
||||
GoogleAdsContext context, string requestId, CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
GoogleAdsClient client = _clientFactory.CreateClient(context);
|
||||
|
||||
GoogleAdsServiceClient googleAdsService =
|
||||
client.GetService(GAdsServices.V22.GoogleAdsService);
|
||||
|
||||
var query = @"
|
||||
SELECT
|
||||
campaign.resource_name,
|
||||
campaign.id,
|
||||
campaign.name,
|
||||
campaign.status,
|
||||
campaign.advertising_channel_type,
|
||||
campaign_budget.amount_micros
|
||||
FROM campaign
|
||||
ORDER BY campaign.name";
|
||||
|
||||
var results = new List<object>();
|
||||
|
||||
var resp = googleAdsService.Search(new SearchGoogleAdsRequest
|
||||
{
|
||||
CustomerId = context.CustomerId,
|
||||
Query = query
|
||||
});
|
||||
|
||||
foreach (var row in resp)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
results.Add(new
|
||||
{
|
||||
resourceName = row.Campaign.ResourceName,
|
||||
id = row.Campaign.Id,
|
||||
name = row.Campaign.Name,
|
||||
status = row.Campaign.Status.ToString(),
|
||||
channelType = row.Campaign.AdvertisingChannelType.ToString(),
|
||||
budgetMicros = row.CampaignBudget?.AmountMicros
|
||||
});
|
||||
}
|
||||
|
||||
return Task.FromResult(ProviderResponse.Success(requestId, new
|
||||
{
|
||||
campaigns = results,
|
||||
totalCount = results.Count,
|
||||
emulated = false
|
||||
}));
|
||||
}
|
||||
catch (GoogleAdsException gex)
|
||||
{
|
||||
_logger.LogError(gex, "Google Ads API error listing campaigns | RequestId={RequestId}", requestId);
|
||||
return Task.FromResult(HandleGoogleAdsException(gex, requestId));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to list campaigns via real API");
|
||||
return Task.FromResult(ProviderResponse.Fail(requestId, "API_ERROR", ex.Message));
|
||||
}
|
||||
}
|
||||
|
||||
private ProviderResponse GetCampaignStats(ProviderRequest request, string requestId)
|
||||
{
|
||||
var payload = request.GetPayload<CampaignStatsPayload>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(payload.CampaignId))
|
||||
return ProviderResponse.Fail(requestId, "VALIDATION", "CampaignId is required");
|
||||
|
||||
_logger.LogInformation("[GoogleAds] EMULATED: Retrieved stats for campaign {CampaignId}", payload.CampaignId);
|
||||
|
||||
return ProviderResponse.Success(requestId, new
|
||||
{
|
||||
campaignId = payload.CampaignId,
|
||||
dateRange = new { start = payload.StartDate ?? "2026-01-01", end = payload.EndDate ?? "2026-01-27" },
|
||||
metrics = new
|
||||
{
|
||||
impressions = 15_234L,
|
||||
clicks = 487L,
|
||||
costMicros = 2_543_000L,
|
||||
conversions = 23,
|
||||
ctr = 0.032,
|
||||
averageCpcMicros = 5_222L
|
||||
},
|
||||
emulated = true
|
||||
});
|
||||
}
|
||||
|
||||
private ProviderResponse GetAccountStats(ProviderRequest request, string requestId)
|
||||
{
|
||||
var payload = request.GetPayload<AccountStatsPayload>();
|
||||
|
||||
_logger.LogInformation("[GoogleAds] EMULATED: Retrieved account stats for tenant {TenantId}", request.TenantId);
|
||||
|
||||
return ProviderResponse.Success(requestId, new
|
||||
{
|
||||
tenantId = request.TenantId,
|
||||
dateRange = new { start = payload.StartDate ?? "2026-01-01", end = payload.EndDate ?? "2026-01-27" },
|
||||
metrics = new
|
||||
{
|
||||
totalCampaigns = 5,
|
||||
activeCampaigns = 3,
|
||||
totalImpressions = 152_340L,
|
||||
totalClicks = 4_870L,
|
||||
totalCostMicros = 25_430_000L,
|
||||
totalConversions = 234
|
||||
},
|
||||
emulated = true
|
||||
});
|
||||
}
|
||||
|
||||
private Task<ProviderResponse> ListAccessibleCustomersAsync(
|
||||
GoogleAdsContext context, string requestId, CancellationToken ct)
|
||||
{
|
||||
if (!_clientFactory.IsRealApiEnabled)
|
||||
{
|
||||
return Task.FromResult(ProviderResponse.Success(requestId, new
|
||||
{
|
||||
customers = new[] { "1234567890", "9876543210" },
|
||||
emulated = true
|
||||
}));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
GoogleAdsClient client = _clientFactory.CreateClient(context);
|
||||
|
||||
CustomerServiceClient customerService =
|
||||
client.GetService(GAdsServices.V22.CustomerService);
|
||||
|
||||
var resp = customerService.ListAccessibleCustomers(new ListAccessibleCustomersRequest());
|
||||
|
||||
var customers = resp.ResourceNames
|
||||
.Select(rn => rn.Split('/').LastOrDefault() ?? rn)
|
||||
.ToArray();
|
||||
|
||||
return Task.FromResult(ProviderResponse.Success(requestId, new
|
||||
{
|
||||
customers,
|
||||
rawResourceNames = resp.ResourceNames.ToArray(),
|
||||
emulated = false
|
||||
}));
|
||||
}
|
||||
catch (GoogleAdsException gex)
|
||||
{
|
||||
_logger.LogError(gex, "Google Ads API error listing accessible customers | RequestId={RequestId}", requestId);
|
||||
return Task.FromResult(HandleGoogleAdsException(gex, requestId));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to list accessible customers");
|
||||
return Task.FromResult(ProviderResponse.Fail(requestId, "API_ERROR", ex.Message));
|
||||
}
|
||||
}
|
||||
|
||||
private static string GenerateId() => Guid.NewGuid().ToString("N")[..12];
|
||||
|
||||
private static AdvertisingChannelTypeEnum.Types.AdvertisingChannelType MapChannelType(CampaignType type)
|
||||
=> type switch
|
||||
{
|
||||
CampaignType.Search => AdvertisingChannelTypeEnum.Types.AdvertisingChannelType.Search,
|
||||
CampaignType.Display => AdvertisingChannelTypeEnum.Types.AdvertisingChannelType.Display,
|
||||
CampaignType.Shopping => AdvertisingChannelTypeEnum.Types.AdvertisingChannelType.Shopping,
|
||||
CampaignType.Video => AdvertisingChannelTypeEnum.Types.AdvertisingChannelType.Video,
|
||||
CampaignType.PerformanceMax => AdvertisingChannelTypeEnum.Types.AdvertisingChannelType.PerformanceMax,
|
||||
_ => AdvertisingChannelTypeEnum.Types.AdvertisingChannelType.Search
|
||||
};
|
||||
|
||||
// ✅ Strategy application that avoids Campaign.MaximizeClicks property dependency
|
||||
private static void ApplyBiddingStrategySafe(Campaign campaign, ModelBiddingStrategy strategy)
|
||||
{
|
||||
// Try to set the enum safely without compile-time dependency on the member name.
|
||||
// Different library/proto generations sometimes change the C# member casing.
|
||||
static BiddingStrategyTypeEnum.Types.BiddingStrategyType ParseBst(params string[] names)
|
||||
{
|
||||
foreach (var n in names)
|
||||
{
|
||||
if (Enum.TryParse<BiddingStrategyTypeEnum.Types.BiddingStrategyType>(n, ignoreCase: true, out var v))
|
||||
return v;
|
||||
}
|
||||
return BiddingStrategyTypeEnum.Types.BiddingStrategyType.Unspecified;
|
||||
}
|
||||
|
||||
campaign.BiddingStrategyType = strategy switch
|
||||
{
|
||||
ModelBiddingStrategy.ManualCpc =>
|
||||
ParseBst("ManualCpc", "MANUAL_CPC"),
|
||||
|
||||
ModelBiddingStrategy.MaximizeClicks =>
|
||||
ParseBst("MaximizeClicks", "MAXIMIZE_CLICKS", "MaximizeClick"),
|
||||
|
||||
ModelBiddingStrategy.MaximizeConversions =>
|
||||
ParseBst("MaximizeConversions", "MAXIMIZE_CONVERSIONS"),
|
||||
|
||||
ModelBiddingStrategy.TargetCpa =>
|
||||
ParseBst("TargetCpa", "TARGET_CPA"),
|
||||
|
||||
ModelBiddingStrategy.TargetRoas =>
|
||||
ParseBst("TargetRoas", "TARGET_ROAS"),
|
||||
|
||||
_ =>
|
||||
ParseBst("MaximizeClicks", "MAXIMIZE_CLICKS", "Unspecified")
|
||||
};
|
||||
|
||||
// Optional: set oneof objects ONLY when you know your generated Campaign has them.
|
||||
// ManualCpc is the most consistently present.
|
||||
if (strategy == ModelBiddingStrategy.ManualCpc)
|
||||
{
|
||||
campaign.ManualCpc = new ManualCpc();
|
||||
}
|
||||
|
||||
// If your Campaign class DOES have these properties in your build, you can uncomment:
|
||||
// if (strategy == ModelBiddingStrategy.MaximizeClicks) campaign.MaximizeClicks = new MaximizeClicks();
|
||||
// if (strategy == ModelBiddingStrategy.MaximizeConversions) campaign.MaximizeConversions = new MaximizeConversions();
|
||||
}
|
||||
|
||||
|
||||
private static ProviderResponse HandleGoogleAdsException(GoogleAdsException gex, string requestId)
|
||||
{
|
||||
var errorDetails = gex.Failure?.Errors?.Select(e => new
|
||||
{
|
||||
errorCode = e.ErrorCode?.ToString(),
|
||||
message = e.Message,
|
||||
trigger = e.Trigger?.StringValue,
|
||||
location = e.Location?.FieldPathElements?.Select(f => f.FieldName).ToArray()
|
||||
}).ToList();
|
||||
|
||||
return ProviderResponse.Fail(requestId, "GOOGLE_ADS_ERROR", gex.Message, new
|
||||
{
|
||||
googleRequestId = gex.RequestId,
|
||||
errors = errorDetails
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user