From 08885c979dceae5d110b2ca3c01da8ea14658c1d Mon Sep 17 00:00:00 2001 From: Jason Sznol Date: Fri, 29 Aug 2025 17:42:24 -0400 Subject: [PATCH 1/6] Update Ad Manager API to 5.10.0 --- dynamicprice/util/src/jvmMain/kotlin/AdManager.jvm.kt | 4 +--- dynamicprice/util/src/jvmMain/kotlin/DynamicPrice.kt | 4 ++-- dynamicprice/util/src/jvmMain/kotlin/Queries.kt | 4 ++-- gradle/libs.versions.toml | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/dynamicprice/util/src/jvmMain/kotlin/AdManager.jvm.kt b/dynamicprice/util/src/jvmMain/kotlin/AdManager.jvm.kt index d64190c..e10c257 100644 --- a/dynamicprice/util/src/jvmMain/kotlin/AdManager.jvm.kt +++ b/dynamicprice/util/src/jvmMain/kotlin/AdManager.jvm.kt @@ -2,12 +2,10 @@ package adsbynimbus.solutions.dynamicprice.util import com.google.api.ads.admanager.axis.factory.* -import com.google.api.ads.admanager.axis.v202408.LineItemCreativeAssociationService -import com.google.api.ads.admanager.axis.v202505.* +import com.google.api.ads.admanager.axis.v202508.* import com.google.api.ads.admanager.lib.client.* import com.google.api.ads.common.lib.auth.* import com.google.api.client.auth.oauth2.* -import kotlinx.coroutines.* actual suspend fun main(args: Array) { val context: AdManagerAxisClient = adManagerContext( diff --git a/dynamicprice/util/src/jvmMain/kotlin/DynamicPrice.kt b/dynamicprice/util/src/jvmMain/kotlin/DynamicPrice.kt index f1d620c..e8d088e 100644 --- a/dynamicprice/util/src/jvmMain/kotlin/DynamicPrice.kt +++ b/dynamicprice/util/src/jvmMain/kotlin/DynamicPrice.kt @@ -1,7 +1,7 @@ package adsbynimbus.solutions.dynamicprice.util -import com.google.api.ads.admanager.axis.v202505.* -import com.google.api.ads.admanager.axis.v202505.CustomTargetingKeyType.* +import com.google.api.ads.admanager.axis.v202508.* +import com.google.api.ads.admanager.axis.v202508.CustomTargetingKeyType.* import java.text.DecimalFormat import kotlinx.coroutines.delay import kotlin.collections.addAll diff --git a/dynamicprice/util/src/jvmMain/kotlin/Queries.kt b/dynamicprice/util/src/jvmMain/kotlin/Queries.kt index 8f8cf8b..571b8f2 100644 --- a/dynamicprice/util/src/jvmMain/kotlin/Queries.kt +++ b/dynamicprice/util/src/jvmMain/kotlin/Queries.kt @@ -1,7 +1,7 @@ package adsbynimbus.solutions.dynamicprice.util -import com.google.api.ads.admanager.axis.utils.v202505.* -import com.google.api.ads.admanager.axis.v202505.* +import com.google.api.ads.admanager.axis.utils.v202508.* +import com.google.api.ads.admanager.axis.v202508.* fun findBy(id: Long): Statement = statement { where("id = :id") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6b9ff7b..d41aafd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -16,7 +16,7 @@ androidx-lifecycle = "2.9.2" androidx-navigation = "2.9.3" androidx-startup = "1.2.0" -api-admanager = "5.9.0" +api-admanager = "5.10.0" compose = "1.9.0" compose-activity = "1.10.1" From d969c72c6850ad3e84165e7d49d931804dc28791 Mon Sep 17 00:00:00 2001 From: Jason Sznol Date: Fri, 29 Aug 2025 18:39:19 -0400 Subject: [PATCH 2/6] Removed unnecessary string template --- dynamicprice/util/src/jvmMain/kotlin/Queries.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dynamicprice/util/src/jvmMain/kotlin/Queries.kt b/dynamicprice/util/src/jvmMain/kotlin/Queries.kt index 571b8f2..9e25e77 100644 --- a/dynamicprice/util/src/jvmMain/kotlin/Queries.kt +++ b/dynamicprice/util/src/jvmMain/kotlin/Queries.kt @@ -10,7 +10,7 @@ fun findBy(id: Long): Statement = statement { fun findBy(name: String): Statement = statement { where("name = :name") - withBindVariableValue("name", "$name") + withBindVariableValue("name", name) }.toStatement() fun findAllBy(name: String): Statement = statement { From a7424d7959b9e4d0e0001f276121ff6d7e385ed7 Mon Sep 17 00:00:00 2001 From: Jason Sznol Date: Fri, 29 Aug 2025 18:54:34 -0400 Subject: [PATCH 3/6] Limit creative associated per API call to 500 --- dynamicprice/util/src/jvmMain/kotlin/DynamicPrice.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dynamicprice/util/src/jvmMain/kotlin/DynamicPrice.kt b/dynamicprice/util/src/jvmMain/kotlin/DynamicPrice.kt index e8d088e..17122cc 100644 --- a/dynamicprice/util/src/jvmMain/kotlin/DynamicPrice.kt +++ b/dynamicprice/util/src/jvmMain/kotlin/DynamicPrice.kt @@ -327,5 +327,8 @@ suspend fun AdManagerAxisClient.associateCreatives( } } } - lineItemCreativeService.createLineItemCreativeAssociations(associations.toTypedArray()) + associations.windowed(size = pageSize, step = pageSize, partialWindows = true).forEach { + lineItemCreativeService.createLineItemCreativeAssociations(it.toTypedArray()) + delay(1000) + } } From c56d81fa57f4bd33fec891dfb06a066015743e3b Mon Sep 17 00:00:00 2001 From: Jason Sznol Date: Fri, 29 Aug 2025 19:40:20 -0400 Subject: [PATCH 4/6] Replaced instance of size > 0 with isNotEmpty --- dynamicprice/util/src/jvmMain/kotlin/DynamicPrice.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dynamicprice/util/src/jvmMain/kotlin/DynamicPrice.kt b/dynamicprice/util/src/jvmMain/kotlin/DynamicPrice.kt index 17122cc..eb0e311 100644 --- a/dynamicprice/util/src/jvmMain/kotlin/DynamicPrice.kt +++ b/dynamicprice/util/src/jvmMain/kotlin/DynamicPrice.kt @@ -217,7 +217,7 @@ suspend fun AdManagerAxisClient.findOrCreateCreatives( """.trimIndent() } } - if (newCreatives.size > 0) { + if (newCreatives.isNotEmpty()) { addAll(creativeService.createCreatives(newCreatives.toTypedArray())) } } From c26a18580ab9c20c63418b96a27fd0124937a644 Mon Sep 17 00:00:00 2001 From: Jason Sznol Date: Mon, 8 Sep 2025 11:34:53 -0400 Subject: [PATCH 5/6] Added script for adaptive banner support --- .../util/src/jvmMain/kotlin/AdaptiveBanner.kt | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 dynamicprice/util/src/jvmMain/kotlin/AdaptiveBanner.kt diff --git a/dynamicprice/util/src/jvmMain/kotlin/AdaptiveBanner.kt b/dynamicprice/util/src/jvmMain/kotlin/AdaptiveBanner.kt new file mode 100644 index 0000000..54a1da0 --- /dev/null +++ b/dynamicprice/util/src/jvmMain/kotlin/AdaptiveBanner.kt @@ -0,0 +1,111 @@ +package adsbynimbus.solutions.dynamicprice.util + +import com.google.api.ads.admanager.axis.v202508.* +import kotlinx.coroutines.delay + +inline val Size.isBanner get() = width == 320 && height == 50 +inline val Size.isInterstitial get() = width == 320 && height == 480 + +suspend fun AdManagerAxisClient.addAdaptiveBannerSupport( + orders: Collection, + sizeId: Long, + bannerCreative: Long, + mrecCreative: Long, + interstitialCreative: Long, + bannerTargetingName: String = "Nimbus Banner", + interstitialTargetingName: String = "Nimbus Interstitial", + +) { + var totalLines = 0 + for (orderId in orders) { + val orderLines = statement { + where("orderId = :orderId") + withBindVariableValue("orderId", orderId) + } + do { + lineItemService.getLineItemsByStatement(orderLines.toStatement()).run { + totalLines = totalResultSetSize + val updates = results?.onEach { line -> + line.creativeTargetings = arrayOf( + CreativeTargeting().also { + it.name = bannerTargetingName + it.targeting = Targeting().apply { + customTargeting = CustomCriteriaSet().apply { + logicalOperator = CustomCriteriaSetLogicalOperator.OR + children = arrayOf( + CustomCriteria().apply { + keyId = sizeId + valueIds = longArrayOf(mrecCreative, interstitialCreative) + operator = CustomCriteriaComparisonOperator.IS_NOT + }, + ) + } + } + }, + CreativeTargeting().also { + it.name = interstitialTargetingName + it.targeting = Targeting().apply { + customTargeting = CustomCriteriaSet().apply { + logicalOperator = CustomCriteriaSetLogicalOperator.OR + children = arrayOf( + CustomCriteria().apply { + keyId = sizeId + valueIds = longArrayOf(bannerCreative, mrecCreative) + operator = CustomCriteriaComparisonOperator.IS_NOT + }, + ) + } + } + } + ) + line.creativePlaceholders.onEach { + when { + it.size.isBanner -> it.targetingName = bannerTargetingName + it.size.isInterstitial -> it.targetingName = interstitialTargetingName + } + } + } + delay(1000) + lineItemService.updateLineItems(updates) + delay(1000) + orderLines.increaseOffsetBy(pageSize) + } + } while (orderLines.offset < totalLines) + totalLines = 0 + } + + val bannerCreative = statement { + where("creativeId = :creativeId") + withBindVariableValue("creativeId", bannerCreative) + } + val interstitialCreative = statement { + where("creativeId = :creativeId") + withBindVariableValue("creativeId", interstitialCreative) + } + totalLines = 0 + do { + lineItemCreativeService.getLineItemCreativeAssociationsByStatement(bannerCreative.toStatement()).run { + totalLines = totalResultSetSize + val updates = results?.onEach { + it.targetingName = bannerTargetingName + } + delay(1000) + lineItemCreativeService.updateLineItemCreativeAssociations(updates) + delay(1000) + bannerCreative.increaseOffsetBy(pageSize) + } + } while (bannerCreative.offset < totalLines) + totalLines = 0 + do { + lineItemCreativeService.getLineItemCreativeAssociationsByStatement(interstitialCreative.toStatement()).run { + totalLines = totalResultSetSize + val updates = results.onEach { + it.apply { targetingName = interstitialTargetingName } + } + delay(1000) + lineItemCreativeService.updateLineItemCreativeAssociations(updates) + delay(1000) + interstitialCreative.increaseOffsetBy(pageSize) + } + } while (interstitialCreative.offset < totalLines) +} From a9628625c85a93ace5b5b0ee8d4b1b7325b41d95 Mon Sep 17 00:00:00 2001 From: Jason Sznol Date: Mon, 8 Sep 2025 21:02:09 -0400 Subject: [PATCH 6/6] Added key-value targeting routine --- .../util/src/jvmMain/kotlin/Targeting.kt | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 dynamicprice/util/src/jvmMain/kotlin/Targeting.kt diff --git a/dynamicprice/util/src/jvmMain/kotlin/Targeting.kt b/dynamicprice/util/src/jvmMain/kotlin/Targeting.kt new file mode 100644 index 0000000..108f8b7 --- /dev/null +++ b/dynamicprice/util/src/jvmMain/kotlin/Targeting.kt @@ -0,0 +1,41 @@ +package adsbynimbus.solutions.dynamicprice.util + +import com.google.api.ads.admanager.axis.v202508.* +import kotlinx.coroutines.delay + +suspend fun AdManagerAxisClient.addTargeting( + orders: Collection, + key: Long, + values: LongArray, +) { + var totalLines = 0 + for (orderId in orders) { + val orderLines = statement { + where("orderId = :orderId") + withBindVariableValue("orderId", orderId) + } + do { + lineItemService.getLineItemsByStatement(orderLines.toStatement()).run { + totalLines = totalResultSetSize + val updates = results?.onEach { line -> + line.targeting.customTargeting?.apply { + children.filterIsInstance().onEach { + if (it.logicalOperator == CustomCriteriaSetLogicalOperator.AND) { + it.children += CustomCriteria().apply { + keyId = key + valueIds = values + operator = CustomCriteriaComparisonOperator.IS_NOT + } + } + } + } + } + delay(1000) + lineItemService.updateLineItems(updates) + delay(1000) + orderLines.increaseOffsetBy(pageSize) + } + } while (orderLines.offset < totalLines) + totalLines = 0 + } +}