diff --git a/example/src/test/java/org/wordpress/android/fluxc/store/blaze/BlazeCampaignsStoreTest.kt b/example/src/test/java/org/wordpress/android/fluxc/store/blaze/BlazeCampaignsStoreTest.kt index 982abca4df..0e4393ea56 100644 --- a/example/src/test/java/org/wordpress/android/fluxc/store/blaze/BlazeCampaignsStoreTest.kt +++ b/example/src/test/java/org/wordpress/android/fluxc/store/blaze/BlazeCampaignsStoreTest.kt @@ -27,6 +27,7 @@ import org.wordpress.android.fluxc.network.rest.wpcom.blaze.CampaignStats import org.wordpress.android.fluxc.network.rest.wpcom.blaze.ContentConfig import org.wordpress.android.fluxc.network.rest.wpcom.blaze.FakeBlazeTargetingRestClient import org.wordpress.android.fluxc.persistence.blaze.BlazeCampaignsDao +import org.wordpress.android.fluxc.persistence.blaze.BlazeCampaignsDao.BlazeAdSuggestionEntity import org.wordpress.android.fluxc.persistence.blaze.BlazeCampaignsDao.BlazeCampaignEntity import org.wordpress.android.fluxc.persistence.blaze.BlazeTargetingDao import org.wordpress.android.fluxc.persistence.blaze.BlazeTargetingDeviceEntity @@ -338,4 +339,39 @@ class BlazeCampaignsStoreTest { assertThat(devices).isNotNull assertThat(devices.size).isEqualTo(10) } + + @Test + fun `when fetching targeting ad suggestions, then persist data in DB`() = test { + whenever(targetingRestClient.fetchBlazeAdSuggestions(any(), any())).thenReturn( + BlazeTargetingPayload( + generateAdSuggestions() + ) + ) + + store.fetchBlazeAdSuggestions(siteModel, 1L) + + verify(blazeCampaignsDao).replaceAdSuggestions(any()) + } + + @Test + fun `when getting ad suggestions, then return data from DB`() = test { + whenever(blazeCampaignsDao.getBlazeAdSuggestions(any(), any())).thenReturn( + generateAdSuggestions() + ) + + val adSuggestions = store.getBlazeAdSuggestions(siteModel, 1L) + + assertThat(adSuggestions).isNotNull + assertThat(adSuggestions.size).isEqualTo(3) + } + + private fun generateAdSuggestions() = List(3) { + BlazeAdSuggestionEntity( + id = it.toString(), + siteId = 1L, + productId = 1L, + tagLine = "Tag line $it", + description = "Description $it" + ) + } } diff --git a/fluxc/schemas/org.wordpress.android.fluxc.persistence.WPAndroidDatabase/25.json b/fluxc/schemas/org.wordpress.android.fluxc.persistence.WPAndroidDatabase/25.json new file mode 100644 index 0000000000..8fc4f3c2cc --- /dev/null +++ b/fluxc/schemas/org.wordpress.android.fluxc.persistence.WPAndroidDatabase/25.json @@ -0,0 +1,1021 @@ +{ + "formatVersion": 1, + "database": { + "version": 25, + "identityHash": "57bb101fbe0aac539a5b7837b3ce7702", + "entities": [ + { + "tableName": "BloggingReminders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localSiteId` INTEGER NOT NULL, `monday` INTEGER NOT NULL, `tuesday` INTEGER NOT NULL, `wednesday` INTEGER NOT NULL, `thursday` INTEGER NOT NULL, `friday` INTEGER NOT NULL, `saturday` INTEGER NOT NULL, `sunday` INTEGER NOT NULL, `hour` INTEGER NOT NULL, `minute` INTEGER NOT NULL, `isPromptRemindersOptedIn` INTEGER NOT NULL, `isPromptsCardOptedIn` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`localSiteId`))", + "fields": [ + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "monday", + "columnName": "monday", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tuesday", + "columnName": "tuesday", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "wednesday", + "columnName": "wednesday", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "thursday", + "columnName": "thursday", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "friday", + "columnName": "friday", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "saturday", + "columnName": "saturday", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sunday", + "columnName": "sunday", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hour", + "columnName": "hour", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "minute", + "columnName": "minute", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPromptRemindersOptedIn", + "columnName": "isPromptRemindersOptedIn", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPromptsCardOptedIn", + "columnName": "isPromptsCardOptedIn", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + } + ], + "primaryKey": { + "columnNames": [ + "localSiteId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "PlanOffers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `internalPlanId` INTEGER NOT NULL, `name` TEXT, `shortName` TEXT, `tagline` TEXT, `description` TEXT, `icon` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "internalPlanId", + "columnName": "internalPlanId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shortName", + "columnName": "shortName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tagline", + "columnName": "tagline", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_PlanOffers_internalPlanId", + "unique": true, + "columnNames": [ + "internalPlanId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_PlanOffers_internalPlanId` ON `${TABLE_NAME}` (`internalPlanId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "PlanOfferIds", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `productId` INTEGER NOT NULL, `internalPlanId` INTEGER NOT NULL, FOREIGN KEY(`internalPlanId`) REFERENCES `PlanOffers`(`internalPlanId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "productId", + "columnName": "productId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "internalPlanId", + "columnName": "internalPlanId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [ + { + "table": "PlanOffers", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "internalPlanId" + ], + "referencedColumns": [ + "internalPlanId" + ] + } + ] + }, + { + "tableName": "PlanOfferFeatures", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `internalPlanId` INTEGER NOT NULL, `stringId` TEXT, `name` TEXT, `description` TEXT, FOREIGN KEY(`internalPlanId`) REFERENCES `PlanOffers`(`internalPlanId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "internalPlanId", + "columnName": "internalPlanId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "stringId", + "columnName": "stringId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [ + { + "table": "PlanOffers", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "internalPlanId" + ], + "referencedColumns": [ + "internalPlanId" + ] + } + ] + }, + { + "tableName": "Comments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `remoteCommentId` INTEGER NOT NULL, `remotePostId` INTEGER NOT NULL, `localSiteId` INTEGER NOT NULL, `remoteSiteId` INTEGER NOT NULL, `authorUrl` TEXT, `authorName` TEXT, `authorEmail` TEXT, `authorProfileImageUrl` TEXT, `authorId` INTEGER NOT NULL, `postTitle` TEXT, `status` TEXT, `datePublished` TEXT, `publishedTimestamp` INTEGER NOT NULL, `content` TEXT, `url` TEXT, `hasParent` INTEGER NOT NULL, `parentId` INTEGER NOT NULL, `iLike` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteCommentId", + "columnName": "remoteCommentId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remotePostId", + "columnName": "remotePostId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteSiteId", + "columnName": "remoteSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "authorUrl", + "columnName": "authorUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "authorName", + "columnName": "authorName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "authorEmail", + "columnName": "authorEmail", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "authorProfileImageUrl", + "columnName": "authorProfileImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "authorId", + "columnName": "authorId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "postTitle", + "columnName": "postTitle", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "datePublished", + "columnName": "datePublished", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "publishedTimestamp", + "columnName": "publishedTimestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hasParent", + "columnName": "hasParent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "parentId", + "columnName": "parentId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "iLike", + "columnName": "iLike", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "DashboardCards", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`siteLocalId` INTEGER NOT NULL, `type` TEXT NOT NULL, `date` TEXT NOT NULL, `json` TEXT NOT NULL, PRIMARY KEY(`siteLocalId`, `type`))", + "fields": [ + { + "fieldPath": "siteLocalId", + "columnName": "siteLocalId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "json", + "columnName": "json", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "siteLocalId", + "type" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "BloggingPrompts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `siteLocalId` INTEGER NOT NULL, `text` TEXT NOT NULL, `date` TEXT NOT NULL, `isAnswered` INTEGER NOT NULL, `respondentsCount` INTEGER NOT NULL, `attribution` TEXT NOT NULL, `respondentsAvatars` TEXT NOT NULL, `answeredLink` TEXT NOT NULL, `bloganuaryId` TEXT, PRIMARY KEY(`date`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "siteLocalId", + "columnName": "siteLocalId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "text", + "columnName": "text", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isAnswered", + "columnName": "isAnswered", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "respondentsCount", + "columnName": "respondentsCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "attribution", + "columnName": "attribution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "respondentsAvatars", + "columnName": "respondentsAvatars", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "answeredLink", + "columnName": "answeredLink", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bloganuaryId", + "columnName": "bloganuaryId", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "date" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "FeatureFlagConfigurations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `value` INTEGER NOT NULL, `created_at` INTEGER NOT NULL, `modified_at` INTEGER NOT NULL, `source` TEXT NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "created_at", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifiedAt", + "columnName": "modified_at", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "source", + "columnName": "source", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "RemoteConfigurations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `value` TEXT NOT NULL, `created_at` INTEGER NOT NULL, `modified_at` INTEGER NOT NULL, `source` TEXT NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "created_at", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifiedAt", + "columnName": "modified_at", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "source", + "columnName": "source", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "JetpackCPConnectedSites", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`remoteSiteId` INTEGER, `localSiteId` INTEGER NOT NULL, `url` TEXT NOT NULL, `name` TEXT NOT NULL, `description` TEXT NOT NULL, `activeJetpackConnectionPlugins` TEXT NOT NULL, PRIMARY KEY(`remoteSiteId`))", + "fields": [ + { + "fieldPath": "remoteSiteId", + "columnName": "remoteSiteId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localSiteId", + "columnName": "localSiteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "activeJetpackConnectionPlugins", + "columnName": "activeJetpackConnectionPlugins", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "remoteSiteId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Domains", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`siteLocalId` INTEGER NOT NULL, `domain` TEXT NOT NULL, `primaryDomain` INTEGER NOT NULL, `wpcomDomain` INTEGER NOT NULL, PRIMARY KEY(`domain`))", + "fields": [ + { + "fieldPath": "siteLocalId", + "columnName": "siteLocalId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "domain", + "columnName": "domain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "primaryDomain", + "columnName": "primaryDomain", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "wpcomDomain", + "columnName": "wpcomDomain", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "domain" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "BlazeCampaigns", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`siteId` INTEGER NOT NULL, `campaignId` INTEGER NOT NULL, `title` TEXT NOT NULL, `imageUrl` TEXT, `createdAt` TEXT NOT NULL, `endDate` TEXT, `uiStatus` TEXT NOT NULL, `budgetCents` INTEGER NOT NULL, `impressions` INTEGER NOT NULL, `clicks` INTEGER NOT NULL, `targetUrn` TEXT, PRIMARY KEY(`siteId`, `campaignId`))", + "fields": [ + { + "fieldPath": "siteId", + "columnName": "siteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "campaignId", + "columnName": "campaignId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "endDate", + "columnName": "endDate", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uiStatus", + "columnName": "uiStatus", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "budgetCents", + "columnName": "budgetCents", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "impressions", + "columnName": "impressions", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "clicks", + "columnName": "clicks", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "targetUrn", + "columnName": "targetUrn", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "siteId", + "campaignId" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_BlazeCampaigns_siteId", + "unique": false, + "columnNames": [ + "siteId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_BlazeCampaigns_siteId` ON `${TABLE_NAME}` (`siteId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "BlazeCampaignsPagination", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`siteId` INTEGER NOT NULL, `page` INTEGER NOT NULL, `totalItems` INTEGER NOT NULL, `totalPages` INTEGER NOT NULL, PRIMARY KEY(`siteId`))", + "fields": [ + { + "fieldPath": "siteId", + "columnName": "siteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "page", + "columnName": "page", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "totalItems", + "columnName": "totalItems", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "totalPages", + "columnName": "totalPages", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "siteId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "JetpackSocial", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`siteLocalId` INTEGER NOT NULL, `isShareLimitEnabled` INTEGER NOT NULL, `toBePublicizedCount` INTEGER NOT NULL, `shareLimit` INTEGER NOT NULL, `publicizedCount` INTEGER NOT NULL, `sharedPostsCount` INTEGER NOT NULL, `sharesRemaining` INTEGER NOT NULL, `isEnhancedPublishingEnabled` INTEGER NOT NULL, `isSocialImageGeneratorEnabled` INTEGER NOT NULL, PRIMARY KEY(`siteLocalId`))", + "fields": [ + { + "fieldPath": "siteLocalId", + "columnName": "siteLocalId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isShareLimitEnabled", + "columnName": "isShareLimitEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "toBePublicizedCount", + "columnName": "toBePublicizedCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shareLimit", + "columnName": "shareLimit", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "publicizedCount", + "columnName": "publicizedCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sharedPostsCount", + "columnName": "sharedPostsCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sharesRemaining", + "columnName": "sharesRemaining", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isEnhancedPublishingEnabled", + "columnName": "isEnhancedPublishingEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isSocialImageGeneratorEnabled", + "columnName": "isSocialImageGeneratorEnabled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "siteLocalId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "BlazeTargetingLanguages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `locale` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "locale", + "columnName": "locale", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "BlazeTargetingDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `locale` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "locale", + "columnName": "locale", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "BlazeTargetingTopics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `description` TEXT NOT NULL, `locale` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "locale", + "columnName": "locale", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "BlazeAdSuggestions", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `siteId` INTEGER NOT NULL, `productId` INTEGER NOT NULL, `tagLine` TEXT NOT NULL, `description` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "siteId", + "columnName": "siteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "productId", + "columnName": "productId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tagLine", + "columnName": "tagLine", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '57bb101fbe0aac539a5b7837b3ce7702')" + ] + } +} \ No newline at end of file diff --git a/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/blaze/FakeBlazeTargetingRestClient.kt b/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/blaze/FakeBlazeTargetingRestClient.kt index cf7cbf2c6a..e2fd3d933a 100644 --- a/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/blaze/FakeBlazeTargetingRestClient.kt +++ b/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/blaze/FakeBlazeTargetingRestClient.kt @@ -4,6 +4,7 @@ import kotlinx.coroutines.delay import org.wordpress.android.fluxc.Payload import org.wordpress.android.fluxc.model.blaze.BlazeTargetingLocation import org.wordpress.android.fluxc.network.rest.wpcom.WPComGsonRequest.WPComGsonNetworkError +import org.wordpress.android.fluxc.persistence.blaze.BlazeCampaignsDao.BlazeAdSuggestionEntity import org.wordpress.android.fluxc.persistence.blaze.BlazeTargetingDeviceEntity import org.wordpress.android.fluxc.persistence.blaze.BlazeTargetingLanguageEntity import org.wordpress.android.fluxc.persistence.blaze.BlazeTargetingTopicEntity @@ -46,6 +47,37 @@ class FakeBlazeTargetingRestClient @Inject constructor() { return BlazeTargetingPayload(generateFakeDevices(locale)) } + + suspend fun fetchBlazeAdSuggestions( + siteId: Long, + productId: Long + ): BlazeTargetingPayload> { + delay(FAKE_DELAY) + + return BlazeTargetingPayload(generateFakeAdSuggestions(siteId, productId)) + } + + @Suppress("MagicNumber") + private fun generateFakeAdSuggestions( + siteId: Long, + productId: Long + ): List { + val adSuggestions = mutableListOf() + + for (i in 1..3) { + adSuggestions.add( + BlazeAdSuggestionEntity( + id = i.toString(), + siteId = siteId, + productId = productId, + tagLine = "Suggested tag line $i", + description = "Suggested description $i" + ) + ) + } + + return adSuggestions + } } data class BlazeTargetingPayload( diff --git a/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WPAndroidDatabase.kt b/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WPAndroidDatabase.kt index b7ebb173f6..3f027723c6 100644 --- a/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WPAndroidDatabase.kt +++ b/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WPAndroidDatabase.kt @@ -16,6 +16,7 @@ import org.wordpress.android.fluxc.persistence.PlanOffersDao.PlanOfferFeature import org.wordpress.android.fluxc.persistence.PlanOffersDao.PlanOfferId import org.wordpress.android.fluxc.persistence.RemoteConfigDao.RemoteConfig import org.wordpress.android.fluxc.persistence.blaze.BlazeCampaignsDao +import org.wordpress.android.fluxc.persistence.blaze.BlazeCampaignsDao.BlazeAdSuggestionEntity import org.wordpress.android.fluxc.persistence.blaze.BlazeCampaignsDao.BlazeCampaignEntity import org.wordpress.android.fluxc.persistence.blaze.BlazeCampaignsDao.BlazeCampaignsPaginationEntity import org.wordpress.android.fluxc.persistence.blaze.BlazeTargetingDao @@ -35,7 +36,7 @@ import org.wordpress.android.fluxc.persistence.jetpacksocial.JetpackSocialDao import org.wordpress.android.fluxc.persistence.jetpacksocial.JetpackSocialDao.JetpackSocialEntity @Database( - version = 24, + version = 25, entities = [ BloggingReminders::class, PlanOffer::class, @@ -54,6 +55,7 @@ import org.wordpress.android.fluxc.persistence.jetpacksocial.JetpackSocialDao.Je BlazeTargetingLanguageEntity::class, BlazeTargetingDeviceEntity::class, BlazeTargetingTopicEntity::class, + BlazeAdSuggestionEntity::class ], autoMigrations = [ AutoMigration(from = 11, to = 12), @@ -63,6 +65,7 @@ import org.wordpress.android.fluxc.persistence.jetpacksocial.JetpackSocialDao.Je AutoMigration(from = 17, to = 18), AutoMigration(from = 22, to = 23), AutoMigration(from = 23, to = 24), + AutoMigration(from = 24, to = 25), ] ) @TypeConverters( diff --git a/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/blaze/BlazeCampaignsDao.kt b/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/blaze/BlazeCampaignsDao.kt index 226e4386bc..7c1d2eeb2f 100644 --- a/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/blaze/BlazeCampaignsDao.kt +++ b/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/blaze/BlazeCampaignsDao.kt @@ -5,6 +5,7 @@ import androidx.room.Entity import androidx.room.Index import androidx.room.Insert import androidx.room.OnConflictStrategy +import androidx.room.PrimaryKey import androidx.room.Query import androidx.room.Transaction import androidx.room.TypeConverters @@ -43,6 +44,24 @@ abstract class BlazeCampaignsDao { @Query("SELECT * from BlazeCampaigns WHERE `siteId` = :siteId ORDER BY createdAt DESC LIMIT 1") abstract fun observeMostRecentCampaignForSite(siteId: Long): Flow + @Query("SELECT * FROM BlazeAdSuggestions WHERE siteId = :siteId AND productId = :productId") + abstract suspend fun getBlazeAdSuggestions( + siteId: Long, + productId: Long + ): List + + @Insert(onConflict = OnConflictStrategy.REPLACE) + abstract suspend fun insertAdSuggestions(suggestions: List) + + @Query("DELETE FROM BlazeAdSuggestions") + abstract suspend fun deleteAdSuggestions() + + @Transaction + open suspend fun replaceAdSuggestions(suggestions: List) { + deleteAdSuggestions() + insertAdSuggestions(suggestions) + } + @Transaction open suspend fun insertCampaignsAndPageInfoForSite( siteId: Long, @@ -159,4 +178,13 @@ abstract class BlazeCampaignsDao { ) } } + + @Entity(tableName = "BlazeAdSuggestions") + data class BlazeAdSuggestionEntity( + @PrimaryKey val id: String, + val siteId: Long, + val productId: Long, + val tagLine: String, + val description: String + ) } diff --git a/fluxc/src/main/java/org/wordpress/android/fluxc/store/blaze/BlazeCampaignsStore.kt b/fluxc/src/main/java/org/wordpress/android/fluxc/store/blaze/BlazeCampaignsStore.kt index ddba4b678e..ca8a8ec165 100644 --- a/fluxc/src/main/java/org/wordpress/android/fluxc/store/blaze/BlazeCampaignsStore.kt +++ b/fluxc/src/main/java/org/wordpress/android/fluxc/store/blaze/BlazeCampaignsStore.kt @@ -179,6 +179,30 @@ class BlazeCampaignsStore @Inject constructor( locale: String = Locale.getDefault().language ) = targetingDao.observeDevices(locale) + suspend fun fetchBlazeAdSuggestions( + siteModel: SiteModel, + productId: Long + ) = coroutineEngine.withDefaultContext( + AppLog.T.API, + this, + "fetch blaze ad suggestions" + ) { + fakeTargetingRestClient.fetchBlazeAdSuggestions(siteModel.siteId, productId).let { payload -> + when { + payload.isError -> BlazeTargetingResult(BlazeTargetingError(payload.error)) + else -> { + campaignsDao.replaceAdSuggestions(payload.data) + BlazeTargetingResult(payload.data) + } + } + } + } + + suspend fun getBlazeAdSuggestions( + siteModel: SiteModel, + productId: Long + ) = campaignsDao.getBlazeAdSuggestions(siteModel.siteId, productId) + data class BlazeCampaignsResult( val model: T? = null, val cached: Boolean = false