twitter-ads 5.2.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -2
  3. data/README.md +1 -1
  4. data/lib/twitter-ads.rb +8 -4
  5. data/lib/twitter-ads/account.rb +6 -24
  6. data/lib/twitter-ads/audiences/tailored_audience.rb +56 -1
  7. data/lib/twitter-ads/{targeting_criteria/behavior_taxonomy.rb → campaign/advertiser_business_categories.rb} +6 -6
  8. data/lib/twitter-ads/campaign/campaign.rb +1 -2
  9. data/lib/twitter-ads/campaign/content_categories.rb +23 -0
  10. data/lib/twitter-ads/campaign/funding_instrument.rb +1 -2
  11. data/lib/twitter-ads/campaign/line_item.rb +4 -4
  12. data/lib/twitter-ads/campaign/organic_tweet.rb +1 -3
  13. data/lib/twitter-ads/campaign/targeting_criteria.rb +1 -1
  14. data/lib/twitter-ads/campaign/tweet.rb +4 -49
  15. data/lib/twitter-ads/client.rb +2 -2
  16. data/lib/twitter-ads/creative/account_media.rb +4 -6
  17. data/lib/twitter-ads/creative/draft_tweet.rb +40 -0
  18. data/lib/twitter-ads/creative/image_app_download_card.rb +2 -2
  19. data/lib/twitter-ads/creative/image_conversation_card.rb +3 -2
  20. data/lib/twitter-ads/creative/media_creative.rb +2 -3
  21. data/lib/twitter-ads/creative/media_library.rb +12 -13
  22. data/lib/twitter-ads/creative/promoted_account.rb +1 -2
  23. data/lib/twitter-ads/creative/promoted_tweet.rb +2 -3
  24. data/lib/twitter-ads/creative/scheduled_tweet.rb +1 -12
  25. data/lib/twitter-ads/creative/tweets.rb +52 -0
  26. data/lib/twitter-ads/creative/video_app_download_card.rb +4 -6
  27. data/lib/twitter-ads/creative/video_conversation_card.rb +6 -6
  28. data/lib/twitter-ads/creative/video_website_card.rb +3 -5
  29. data/lib/twitter-ads/creative/website_card.rb +2 -2
  30. data/lib/twitter-ads/cursor.rb +6 -0
  31. data/lib/twitter-ads/enum.rb +22 -13
  32. data/lib/twitter-ads/error.rb +5 -15
  33. data/lib/twitter-ads/http/request.rb +37 -2
  34. data/lib/twitter-ads/http/response.rb +1 -13
  35. data/lib/twitter-ads/resources/analytics.rb +99 -47
  36. data/lib/twitter-ads/resources/dsl.rb +8 -1
  37. data/lib/twitter-ads/restapi.rb +29 -0
  38. data/lib/twitter-ads/settings/tax.rb +13 -1
  39. data/lib/twitter-ads/targeting/audience_summary.rb +47 -0
  40. data/lib/twitter-ads/targeting_criteria/{behavior.rb → conversation.rb} +3 -7
  41. data/lib/twitter-ads/targeting_criteria/event.rb +1 -0
  42. data/lib/twitter-ads/utils.rb +23 -0
  43. data/lib/twitter-ads/version.rb +1 -1
  44. data/spec/fixtures/audience_summary.json +14 -0
  45. data/spec/fixtures/line_items_all.json +2 -10
  46. data/spec/fixtures/line_items_load.json +0 -1
  47. data/spec/fixtures/tailored_audiences_all.json +3 -0
  48. data/spec/fixtures/targeted_audiences.json +33 -0
  49. data/spec/fixtures/tweet_previews.json +23 -0
  50. data/spec/spec_helper.rb +1 -4
  51. data/spec/twitter-ads/audiences/tailored_audience_spec.rb +25 -2
  52. data/spec/twitter-ads/campaign/line_item_spec.rb +0 -1
  53. data/spec/twitter-ads/campaign/targeting_criteria_spec.rb +1 -1
  54. data/spec/twitter-ads/campaign/tweet_spec.rb +0 -59
  55. data/spec/twitter-ads/client_spec.rb +17 -1
  56. data/spec/twitter-ads/creative/media_creative_spec.rb +1 -1
  57. data/spec/twitter-ads/creative/promoted_tweet_spec.rb +18 -0
  58. data/spec/twitter-ads/creative/tweet_previews_spec.rb +41 -0
  59. data/spec/twitter-ads/rate_limit_spec.rb +247 -0
  60. data/spec/twitter-ads/retry_count_spec.rb +61 -0
  61. data/spec/twitter-ads/{creative/image_app_download_card_spec.rb → targeting/audience_summary_spec.rb} +16 -18
  62. metadata +50 -49
  63. data/lib/twitter-ads/audiences/audience_intelligence.rb +0 -68
  64. data/lib/twitter-ads/targeting/reach_estimate.rb +0 -78
  65. data/spec/fixtures/tweet_preview.json +0 -24
  66. data/spec/twitter-ads/campaign/reach_estimate_spec.rb +0 -103
  67. data/spec/twitter-ads/creative/account_media_spec.rb +0 -32
  68. data/spec/twitter-ads/creative/image_conversation_card_spec.rb +0 -40
  69. data/spec/twitter-ads/creative/video_app_download_card_spec.rb +0 -42
  70. data/spec/twitter-ads/creative/video_conversation_card_spec.rb +0 -51
  71. data/spec/twitter-ads/creative/website_card_spec.rb +0 -42
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2019 Twitter, Inc.
3
+
4
+ module TwitterAds
5
+ module AudienceSummary
6
+
7
+ include TwitterAds::DSL
8
+ include TwitterAds::Resource
9
+
10
+ RESOURCE = "/#{TwitterAds::API_VERSION}/" \
11
+ 'accounts/%{account_id}/audience_summary'
12
+
13
+ property :audience_size, read_only: true
14
+
15
+ class << self
16
+
17
+ # Get an audience summary for the specified targeting criteria.
18
+ #
19
+ # @example
20
+ # TwitterAds::AudienceSummary.fetch(
21
+ # account,
22
+ # params: {targeting_criteria:[{targeting_type:'LOCATION',
23
+ # targeting_value:'96683cc9126741d1'}]}
24
+ # )
25
+ #
26
+ # @param params [Hash] A hash of input targeting criteria values
27
+ #
28
+ # @return [Hash] A hash containing the min and max audience size.
29
+ #
30
+ # @since 7.0.0
31
+ # @see https://developer.twitter.com/en/docs/ads/campaign-management/api-reference/audience-summary
32
+ def fetch(account, params)
33
+ resource = RESOURCE % { account_id: account.id }
34
+ headers = { 'Content-Type' => 'application/json' }
35
+
36
+ response = TwitterAds::Request.new(account.client,
37
+ :post,
38
+ resource,
39
+ headers: headers,
40
+ body: params.to_json).perform
41
+ response.body[:data]
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+ end
@@ -2,22 +2,18 @@
2
2
  # Copyright (C) 2019 Twitter, Inc.
3
3
 
4
4
  module TwitterAds
5
- class Behavior
5
+ class Conversation
6
6
 
7
7
  include TwitterAds::DSL
8
8
  include TwitterAds::Resource
9
9
 
10
- property :id, read_only: true
11
10
  property :name, read_only: true
12
11
  property :targeting_type, read_only: true
13
12
  property :targeting_value, read_only: true
14
- property :audience_code, read_only: true
15
- property :country_code, read_only: true
16
- property :partner_source, read_only: true
17
- property :targetable_type, read_only: true
13
+ property :conversation_type, read_only: true
18
14
 
19
15
  RESOURCE_COLLECTION = "/#{TwitterAds::API_VERSION}/" \
20
- 'targeting_criteria/behaviors' # @api private
16
+ 'targeting_criteria/conversations' # @api private
21
17
 
22
18
  def initialize(account)
23
19
  @account = account
@@ -21,6 +21,7 @@ module TwitterAds
21
21
  property :gender_breakdown_percentage, read_only: true
22
22
  property :device_breakdown_percentage, read_only: true
23
23
  property :country_breakdown_percentage, read_only: true
24
+ property :targeting_value, read_only: true
24
25
 
25
26
  RESOURCE_COLLECTION = "/#{TwitterAds::API_VERSION}/" \
26
27
  'targeting_criteria/events' # @api private
@@ -70,6 +70,29 @@ module TwitterAds
70
70
  warn message
71
71
  end
72
72
 
73
+ def extract_response_headers(headers)
74
+ values = {}
75
+ # only get "X-${name}" custom response headers
76
+ headers.each { |key, value|
77
+ if key =~ /^x-/
78
+ values[key.gsub(/^x-/, '').tr('-', '_')] = \
79
+ value.first =~ /^[0-9]*$/ ? value.first.to_i : value.first
80
+ end
81
+ }
82
+ values
83
+ end
84
+
85
+ def flatten_params(args)
86
+ params = args
87
+ params.each { |key, value|
88
+ if value.is_a?(Array)
89
+ next if value.empty?
90
+ params[key] = value.join(',')
91
+ end
92
+ }
93
+ params
94
+ end
95
+
73
96
  end
74
97
 
75
98
  end
@@ -2,5 +2,5 @@
2
2
  # Copyright (C) 2019 Twitter, Inc.
3
3
 
4
4
  module TwitterAds
5
- VERSION = '5.2.0'
5
+ VERSION = '8.0.0'
6
6
  end
@@ -0,0 +1,14 @@
1
+ {
2
+ "request": {
3
+ "params": {
4
+ "targeting_criteria": null,
5
+ "account_id": "2iqph"
6
+ }
7
+ },
8
+ "data": {
9
+ "audience_size": {
10
+ "min": 41133600,
11
+ "max": 50274400
12
+ }
13
+ }
14
+ }
@@ -29,7 +29,6 @@
29
29
  "currency": "USD",
30
30
  "created_at": "2011-07-11T20:36:11Z",
31
31
  "updated_at": "2011-09-04T19:39:51Z",
32
- "include_sentiment": null,
33
32
  "campaign_id": "2wap7",
34
33
  "deleted": false
35
34
  },
@@ -57,7 +56,6 @@
57
56
  "currency": "USD",
58
57
  "created_at": "2011-07-13T20:56:39Z",
59
58
  "updated_at": "2011-09-04T19:39:04Z",
60
- "include_sentiment": null,
61
59
  "campaign_id": "2wamv",
62
60
  "deleted": false
63
61
  },
@@ -85,7 +83,6 @@
85
83
  "currency": "USD",
86
84
  "created_at": "2011-07-14T00:04:47Z",
87
85
  "updated_at": "2011-09-04T19:39:39Z",
88
- "include_sentiment": null,
89
86
  "campaign_id": "2wai9",
90
87
  "deleted": false
91
88
  },
@@ -113,7 +110,6 @@
113
110
  "currency": "USD",
114
111
  "created_at": "2011-08-22T22:42:18Z",
115
112
  "updated_at": "2011-09-04T19:40:02Z",
116
- "include_sentiment": null,
117
113
  "campaign_id": "2of1n",
118
114
  "deleted": false
119
115
  },
@@ -141,7 +137,7 @@
141
137
  "currency": "JPY",
142
138
  "created_at": "2011-08-26T20:51:14Z",
143
139
  "updated_at": "2011-08-26T21:30:25Z",
144
- "include_sentiment": "POSITIVE_ONLY",
140
+
145
141
  "campaign_id": "2w9n1",
146
142
  "deleted": true
147
143
  },
@@ -169,7 +165,7 @@
169
165
  "currency": "USD",
170
166
  "created_at": "2011-08-26T21:38:51Z",
171
167
  "updated_at": "2011-08-26T22:24:37Z",
172
- "include_sentiment": "POSITIVE_ONLY",
168
+
173
169
  "campaign_id": "2vuug",
174
170
  "deleted": true
175
171
  },
@@ -197,7 +193,6 @@
197
193
  "currency": "JPY",
198
194
  "created_at": "2011-08-26T22:28:55Z",
199
195
  "updated_at": "2011-09-04T19:38:46Z",
200
- "include_sentiment": null,
201
196
  "campaign_id": "2vuj3",
202
197
  "deleted": false
203
198
  },
@@ -225,7 +220,6 @@
225
220
  "currency": "USD",
226
221
  "created_at": "2011-09-01T17:25:04Z",
227
222
  "updated_at": "2011-09-16T02:56:55Z",
228
- "include_sentiment": null,
229
223
  "campaign_id": "2v3c4",
230
224
  "deleted": true
231
225
  },
@@ -253,7 +247,6 @@
253
247
  "currency": "JPY",
254
248
  "created_at": "2011-09-06T17:42:52Z",
255
249
  "updated_at": "2011-09-30T18:54:18Z",
256
- "include_sentiment": null,
257
250
  "campaign_id": "2ttv3",
258
251
  "deleted": false
259
252
  },
@@ -281,7 +274,6 @@
281
274
  "currency": "USD",
282
275
  "created_at": "2011-09-06T22:44:13Z",
283
276
  "updated_at": "2011-09-20T01:32:27Z",
284
- "include_sentiment": null,
285
277
  "campaign_id": "2ttv3",
286
278
  "deleted": true
287
279
  }
@@ -24,7 +24,6 @@
24
24
  "currency": "USD",
25
25
  "created_at": "2011-07-11T20:36:11Z",
26
26
  "updated_at": "2011-09-04T19:39:51Z",
27
- "include_sentiment": null,
28
27
  "campaign_id": "2wap7",
29
28
  "deleted": false
30
29
  },
@@ -14,6 +14,7 @@
14
14
  ],
15
15
  "audience_type": "WEB",
16
16
  "id": "abc2",
17
+ "owner_account_id": "18ce54uhdu0",
17
18
  "reasons_not_targetable": [
18
19
  "TOO_SMALL"
19
20
  ],
@@ -33,6 +34,7 @@
33
34
  ],
34
35
  "audience_type": "CRM",
35
36
  "id": "abc1",
37
+ "owner_account_id": "18ce54uhdu0",
36
38
  "reasons_not_targetable": [],
37
39
  "list_type": "DEVICE_ID",
38
40
  "created_at": "2014-05-22T17:37:12Z",
@@ -50,6 +52,7 @@
50
52
  ],
51
53
  "audience_type": "CRM",
52
54
  "id": "abc3",
55
+ "owner_account_id": "18ce54uhdu0",
53
56
  "reasons_not_targetable": [
54
57
  "TOO_SMALL"
55
58
  ],
@@ -0,0 +1,33 @@
1
+ {
2
+ "request": {
3
+ "params": {
4
+ "account_id": "2iqph",
5
+ "tailored_audience_id": "abc2"
6
+ }
7
+ },
8
+ "next_cursor": null,
9
+ "data": [
10
+ {
11
+ "campaign_id": "59hod",
12
+ "campaign_name": "test-campaign",
13
+ "line_items": [
14
+ {
15
+ "id": "5gzog",
16
+ "name": "test-line-item",
17
+ "servable": true
18
+ }
19
+ ]
20
+ },
21
+ {
22
+ "campaign_id": "arja7",
23
+ "campaign_name": "Untitled campaign",
24
+ "line_items": [
25
+ {
26
+ "id": "bjw1q",
27
+ "name": null,
28
+ "servable": true
29
+ }
30
+ ]
31
+ }
32
+ ]
33
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "data_type": "tweet_previews",
3
+ "request": {
4
+ "params": {
5
+ "tweet_ids": [
6
+ "1130942781109596160",
7
+ "1101254234031370240"
8
+ ],
9
+ "tweet_type": "PUBLISHED",
10
+ "account_id": "2iqph"
11
+ }
12
+ },
13
+ "data": [
14
+ {
15
+ "tweet_id": "1130942781109596160",
16
+ "preview": "<iframe class='tweet-preview' src='https://ton.smf1.twitter.com/ads-manager/tweet-preview/index.html?data=c29tZSByYW5kb20gYmFzZTY0IHN0cmluZ3MgaGVyZS4uLg=='>"
17
+ },
18
+ {
19
+ "tweet_id": "1101254234031370240",
20
+ "preview": "<iframe class='tweet-preview' src='https://ton.smf1.twitter.com/ads-manager/tweet-preview/index.html?data=c29tZSByYW5kb20gYmFzZTY0IHN0cmluZ3MgaGVyZS4uLg=='>"
21
+ }
22
+ ]
23
+ }
@@ -3,12 +3,10 @@
3
3
 
4
4
  unless RUBY_PLATFORM =~ /java/ || RUBY_ENGINE =~ /rbx/
5
5
  require 'simplecov'
6
- require 'codeclimate-test-reporter'
7
6
 
8
7
  SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
9
8
  [
10
- SimpleCov::Formatter::HTMLFormatter,
11
- CodeClimate::TestReporter::Formatter
9
+ SimpleCov::Formatter::HTMLFormatter
12
10
  ]
13
11
  )
14
12
 
@@ -22,7 +20,6 @@ require 'rubocop'
22
20
  require 'faker'
23
21
 
24
22
  require 'webmock/rspec'
25
- WebMock.disable_net_connect!(allow: 'codeclimate.com')
26
23
 
27
24
  require 'twitter-ads'
28
25
  include TwitterAds
@@ -7,7 +7,12 @@ describe TwitterAds::TailoredAudience do
7
7
 
8
8
  before(:each) do
9
9
  stub_fixture(:get, :accounts_all, "#{ADS_API}/accounts")
10
- stub_fixture(:get, :accounts_load, "#{ADS_API}/accounts/2iqph")
10
+ stub_fixture(:get,
11
+ :tailored_audiences_load,
12
+ "#{ADS_API}/accounts/2iqph/tailored_audiences/abc2?with_deleted=true")
13
+ stub_fixture(:get,
14
+ :targeted_audiences,
15
+ "#{ADS_API}/accounts/2iqph/tailored_audiences/abc2/targeted")
11
16
  end
12
17
 
13
18
  let(:client) do
@@ -20,7 +25,7 @@ describe TwitterAds::TailoredAudience do
20
25
  end
21
26
 
22
27
  let(:account) { client.accounts.first }
23
-
28
+ let(:tailored_audience) { described_class.load(account, 'abc2') }
24
29
  # check model properties
25
30
  subject { described_class.new(account) }
26
31
 
@@ -29,6 +34,7 @@ describe TwitterAds::TailoredAudience do
29
34
  created_at
30
35
  updated_at
31
36
  deleted
37
+ owner_account_id
32
38
  audience_size
33
39
  audience_type
34
40
  metadata
@@ -42,4 +48,21 @@ describe TwitterAds::TailoredAudience do
42
48
 
43
49
  include_examples 'object property check', read, write
44
50
 
51
+ describe '#targeted' do
52
+
53
+ let(:cursor) { tailored_audience.targeted }
54
+
55
+ it 'has all the correct properties' do
56
+ result = cursor.first
57
+ expect(result).to eq(cursor.instance_variable_get('@collection').first)
58
+ expect(result).to be_instance_of(TwitterAds::TargetedTailoredAudience)
59
+ expect(cursor).to be_instance_of(Cursor)
60
+ end
61
+
62
+ it 'raises error when TailoredAudience is not loaded' do
63
+ result = TwitterAds::TailoredAudience.new(account)
64
+ expect(result).to receive(:validate_loaded).and_call_original
65
+ expect { result.targeted }.to raise_error(ArgumentError)
66
+ end
67
+ end
45
68
  end
@@ -32,7 +32,6 @@ describe TwitterAds::LineItem do
32
32
  advertiser_domain
33
33
  categories
34
34
  charge_by
35
- include_sentiment
36
35
  objective
37
36
  entity_status
38
37
  primary_web_event_tag
@@ -30,8 +30,8 @@ describe TwitterAds::TargetingCriteria do
30
30
  line_item_id
31
31
  targeting_type
32
32
  targeting_value
33
+ operator_type
33
34
  tailored_audience_expansion
34
- tailored_audience_type
35
35
  )
36
36
 
37
37
  include_examples 'object property check', read, write
@@ -21,63 +21,4 @@ describe TwitterAds::Tweet do
21
21
 
22
22
  let(:account) { client.accounts.first }
23
23
 
24
- describe '#tweet_preview' do
25
-
26
- let!(:resource_collection) { "#{ADS_API}/accounts/#{account.id}/tweet/preview" }
27
-
28
- before(:each) do
29
- stub_fixture(:get, :tweet_preview, /#{resource_collection}.*/)
30
- end
31
-
32
- context 'with an existing tweet id' do
33
-
34
- it 'successfully returns a preview of the specified tweet' do
35
- params = { id: 634798319504617472 }
36
- result = subject.preview(account, params)
37
- expect(result.size).not_to be_nil
38
- expect(result).to all(include(:platform, :preview))
39
- end
40
-
41
- end
42
-
43
- context 'when previewing a new tweet' do
44
-
45
- it 'url encodes the status content' do
46
- params = { text: 'Hello World!', card_id: '19v69' }
47
- expect(URI).to receive(:escape).at_least(:once).and_call_original
48
- result = subject.preview(account, params)
49
- expect(result.size).not_to be_nil
50
- expect(result).to all(include(:platform, :preview))
51
- end
52
-
53
- it 'allows a single value for the media_ids param' do
54
- resource = "/#{TwitterAds::API_VERSION}/accounts/#{account.id}/tweet/preview"
55
- expected = { text: 'Hello%20World!', media_ids: 634458428836962304 }
56
-
57
- expect(TwitterAds::Request).to receive(:new).with(
58
- account.client, :get, resource, params: expected).and_call_original
59
-
60
- params = { text: 'Hello World!', media_ids: 634458428836962304 }
61
- result = subject.preview(account, params)
62
- expect(result.size).not_to be_nil
63
- expect(result).to all(include(:platform, :preview))
64
- end
65
-
66
- it 'allows an array of values for the media_ids param' do
67
- resource = "/#{TwitterAds::API_VERSION}/accounts/#{account.id}/tweet/preview"
68
- expected = { text: 'Hello%20World!', media_ids: '634458428836962304,634458428836962305' }
69
-
70
- expect(TwitterAds::Request).to receive(:new).with(
71
- account.client, :get, resource, params: expected).and_call_original
72
-
73
- params = { text: 'Hello World!', media_ids: [634458428836962304, 634458428836962305] }
74
- result = subject.preview(account, params)
75
- expect(result.size).not_to be_nil
76
- expect(result).to all(include(:platform, :preview))
77
- end
78
-
79
- end
80
-
81
- end
82
-
83
24
  end