diff --git a/examples/batch_request.rb b/examples/batch_request.rb index ea9dfdfa..1422d0cc 100644 --- a/examples/batch_request.rb +++ b/examples/batch_request.rb @@ -46,7 +46,7 @@ line_item_1.name = 'my first ad' line_item_1.product_type = TwitterAds::Product::PROMOTED_TWEETS line_item_1.placements = [TwitterAds::Placement::ALL_ON_TWITTER] -line_item_1.objective = TwitterAds::Objective::TWEET_ENGAGEMENTS +line_item_1.objective = TwitterAds::Objective::ENGAGEMENTS line_item_1.bid_amount_local_micro = 10_000 line_item_1.entity_status = EntityStatus::PAUSED @@ -55,7 +55,7 @@ line_item_2.name = 'my second ad' line_item_2.product_type = TwitterAds::Product::PROMOTED_TWEETS line_item_2.placements = [TwitterAds::Placement::ALL_ON_TWITTER] -line_item_2.objective = TwitterAds::Objective::TWEET_ENGAGEMENTS +line_item_2.objective = TwitterAds::Objective::ENGAGEMENTS line_item_2.bid_amount_local_micro = 20_000 line_item_2.entity_status = EntityStatus::PAUSED diff --git a/examples/metric_filtering.rb b/examples/metric_filtering.rb index 9c5f2909..f45abfa6 100644 --- a/examples/metric_filtering.rb +++ b/examples/metric_filtering.rb @@ -202,7 +202,7 @@ class Metrics :video, :other ], - tweet_engagements: [ + engagements: [ :conversion, :engagement, :media, diff --git a/examples/quick_start.rb b/examples/quick_start.rb index 1096e06e..126441b0 100644 --- a/examples/quick_start.rb +++ b/examples/quick_start.rb @@ -36,7 +36,7 @@ line_item.name = 'my first ad' line_item.product_type = Product::PROMOTED_TWEETS line_item.placements = [Placement::ALL_ON_TWITTER] -line_item.objective = Objective::TWEET_ENGAGEMENTS +line_item.objective = Objective::ENGAGEMENTS line_item.bid_amount_local_micro = 10_000 line_item.entity_status = EntityStatus::PAUSED line_item.save diff --git a/lib/twitter-ads/audiences/tailored_audience.rb b/lib/twitter-ads/audiences/tailored_audience.rb index 4071606b..3222e37d 100644 --- a/lib/twitter-ads/audiences/tailored_audience.rb +++ b/lib/twitter-ads/audiences/tailored_audience.rb @@ -20,6 +20,7 @@ class TailoredAudience property :audience_size, read_only: true property :audience_type, read_only: true property :metadata, read_only: true + property :owner_account_id, read_only: true property :partner_source, read_only: true property :reasons_not_targetable, read_only: true property :targetable, type: :bool, read_only: true @@ -91,7 +92,7 @@ def delete! from_response(response.body[:data]) end - # This is a private API and requires whitelisting from Twitter. + # This is a private API and requires allowlisting from Twitter. # # This endpoint will allow partners to add, update and remove users from a given # tailored_audience_id. @@ -137,6 +138,60 @@ def users(params) [success_count, total_count] end + # Retrieves the entites targeting the current tailored audience instance. + # + # @example + # audience.targeted(with_active=true) + # + # @param with_active [bool] Include active/inactive + # + # @since 8.0.0 + # + # @return [self] Returns a Cursor instance of the targeted entities. + def targeted(opts = {}) + validate_loaded + params = {}.merge!(opts) + TargetedTailoredAudience.load(account, id, params) + end + + def validate_loaded + raise ArgumentError.new( + "Error! #{self.class} object not yet initialized, " \ + "call #{self.class}.load first") if id.nil? + end + end + + class TargetedTailoredAudience + + include TwitterAds::DSL + include TwitterAds::Resource + + attr_reader :account + + # read-only + property :campaign_id, read_only: true + property :campaign_name, read_only: true + property :line_items, read_only: true + + RESOURCE_TARGETED = "/#{TwitterAds::API_VERSION}/" \ + 'accounts/%{account_id}/tailored_audiences/%{id}/targeted' # @api private + + def initialize(account) + @account = account + self + end + + class << self + + def load(account, tailored_audience_id, params) + resource = RESOURCE_TARGETED % { account_id: account.id, id: tailored_audience_id } + request = TwitterAds::Request.new(account.client, + :get, + resource, + params: params) + Cursor.new(self, request, init_with: [account]) + end + end end class TailoredAudiencePermission diff --git a/lib/twitter-ads/client.rb b/lib/twitter-ads/client.rb index b8e4646d..8ac84189 100644 --- a/lib/twitter-ads/client.rb +++ b/lib/twitter-ads/client.rb @@ -3,7 +3,7 @@ module TwitterAds - API_VERSION = '7' + API_VERSION = '8' # The Ads API Client class which functions as a # container for basic API consumer information. diff --git a/lib/twitter-ads/enum.rb b/lib/twitter-ads/enum.rb index de345be1..f0eec3e2 100644 --- a/lib/twitter-ads/enum.rb +++ b/lib/twitter-ads/enum.rb @@ -9,13 +9,12 @@ module Enum module Objective APP_ENGAGEMENTS = 'APP_ENGAGEMENTS' APP_INSTALLS = 'APP_INSTALLS' + ENGAGEMENTS = 'ENGAGEMENTS' FOLLOWERS = 'FOLLOWERS' - LEAD_GENERATION = 'LEAD_GENERATION' - TWEET_ENGAGEMENTS = 'TWEET_ENGAGEMENTS' + PREROLL_VIEWS = 'PREROLL_VIEWS' + REACH = 'REACH' VIDEO_VIEWS = 'VIDEO_VIEWS' WEBSITE_CLICKS = 'WEBSITE_CLICKS' - WEBSITE_CONVERSIONS = 'WEBSITE_CONVERSIONS' - end module Product diff --git a/lib/twitter-ads/version.rb b/lib/twitter-ads/version.rb index 0ecdbe53..a75c884e 100644 --- a/lib/twitter-ads/version.rb +++ b/lib/twitter-ads/version.rb @@ -2,5 +2,5 @@ # Copyright (C) 2019 Twitter, Inc. module TwitterAds - VERSION = '7.0.1' + VERSION = '8.0.0' end diff --git a/spec/fixtures/tailored_audiences_all.json b/spec/fixtures/tailored_audiences_all.json index 64563c73..a96c87bd 100644 --- a/spec/fixtures/tailored_audiences_all.json +++ b/spec/fixtures/tailored_audiences_all.json @@ -14,6 +14,7 @@ ], "audience_type": "WEB", "id": "abc2", + "owner_account_id": "18ce54uhdu0", "reasons_not_targetable": [ "TOO_SMALL" ], @@ -33,6 +34,7 @@ ], "audience_type": "CRM", "id": "abc1", + "owner_account_id": "18ce54uhdu0", "reasons_not_targetable": [], "list_type": "DEVICE_ID", "created_at": "2014-05-22T17:37:12Z", @@ -50,6 +52,7 @@ ], "audience_type": "CRM", "id": "abc3", + "owner_account_id": "18ce54uhdu0", "reasons_not_targetable": [ "TOO_SMALL" ], diff --git a/spec/fixtures/targeted_audiences.json b/spec/fixtures/targeted_audiences.json new file mode 100644 index 00000000..0b711004 --- /dev/null +++ b/spec/fixtures/targeted_audiences.json @@ -0,0 +1,33 @@ +{ + "request": { + "params": { + "account_id": "2iqph", + "tailored_audience_id": "abc2" + } + }, + "next_cursor": null, + "data": [ + { + "campaign_id": "59hod", + "campaign_name": "test-campaign", + "line_items": [ + { + "id": "5gzog", + "name": "test-line-item", + "servable": true + } + ] + }, + { + "campaign_id": "arja7", + "campaign_name": "Untitled campaign", + "line_items": [ + { + "id": "bjw1q", + "name": null, + "servable": true + } + ] + } + ] +} \ No newline at end of file diff --git a/spec/twitter-ads/audiences/tailored_audience_spec.rb b/spec/twitter-ads/audiences/tailored_audience_spec.rb index 4efd2022..5f844392 100644 --- a/spec/twitter-ads/audiences/tailored_audience_spec.rb +++ b/spec/twitter-ads/audiences/tailored_audience_spec.rb @@ -7,7 +7,12 @@ before(:each) do stub_fixture(:get, :accounts_all, "#{ADS_API}/accounts") - stub_fixture(:get, :accounts_load, "#{ADS_API}/accounts/2iqph") + stub_fixture(:get, + :tailored_audiences_load, + "#{ADS_API}/accounts/2iqph/tailored_audiences/abc2?with_deleted=true") + stub_fixture(:get, + :targeted_audiences, + "#{ADS_API}/accounts/2iqph/tailored_audiences/abc2/targeted") end let(:client) do @@ -20,7 +25,7 @@ end let(:account) { client.accounts.first } - + let(:tailored_audience) { described_class.load(account, 'abc2') } # check model properties subject { described_class.new(account) } @@ -29,6 +34,7 @@ created_at updated_at deleted + owner_account_id audience_size audience_type metadata @@ -42,4 +48,21 @@ include_examples 'object property check', read, write + describe '#targeted' do + + let(:cursor) { tailored_audience.targeted } + + it 'has all the correct properties' do + result = cursor.first + expect(result).to eq(cursor.instance_variable_get('@collection').first) + expect(result).to be_instance_of(TwitterAds::TargetedTailoredAudience) + expect(cursor).to be_instance_of(Cursor) + end + + it 'raises error when TailoredAudience is not loaded' do + result = TwitterAds::TailoredAudience.new(account) + expect(result).to receive(:validate_loaded).and_call_original + expect { result.targeted }.to raise_error(ArgumentError) + end + end end