twitter-ads 5.2.0 → 6.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.
- checksums.yaml +4 -4
- data/lib/twitter-ads.rb +4 -1
- data/lib/twitter-ads/account.rb +9 -8
- data/lib/twitter-ads/campaign/campaign.rb +1 -2
- data/lib/twitter-ads/campaign/funding_instrument.rb +1 -2
- data/lib/twitter-ads/campaign/line_item.rb +2 -3
- data/lib/twitter-ads/campaign/organic_tweet.rb +1 -3
- data/lib/twitter-ads/campaign/targeting_criteria.rb +0 -1
- data/lib/twitter-ads/campaign/tweet.rb +4 -49
- data/lib/twitter-ads/client.rb +2 -2
- data/lib/twitter-ads/creative/account_media.rb +4 -6
- data/lib/twitter-ads/creative/draft_tweet.rb +40 -0
- data/lib/twitter-ads/creative/image_app_download_card.rb +2 -2
- data/lib/twitter-ads/creative/image_conversation_card.rb +3 -2
- data/lib/twitter-ads/creative/media_creative.rb +1 -2
- data/lib/twitter-ads/creative/media_library.rb +2 -4
- data/lib/twitter-ads/creative/promoted_account.rb +1 -2
- data/lib/twitter-ads/creative/promoted_tweet.rb +1 -2
- data/lib/twitter-ads/creative/scheduled_tweet.rb +1 -12
- data/lib/twitter-ads/creative/tweets.rb +52 -0
- data/lib/twitter-ads/creative/video_app_download_card.rb +4 -6
- data/lib/twitter-ads/creative/video_conversation_card.rb +6 -6
- data/lib/twitter-ads/creative/video_website_card.rb +3 -5
- data/lib/twitter-ads/creative/website_card.rb +2 -2
- data/lib/twitter-ads/cursor.rb +6 -0
- data/lib/twitter-ads/enum.rb +10 -5
- data/lib/twitter-ads/error.rb +5 -15
- data/lib/twitter-ads/http/request.rb +30 -1
- data/lib/twitter-ads/http/response.rb +1 -13
- data/lib/twitter-ads/resources/analytics.rb +99 -47
- data/lib/twitter-ads/resources/dsl.rb +8 -1
- data/lib/twitter-ads/restapi.rb +29 -0
- data/lib/twitter-ads/settings/tax.rb +13 -1
- data/lib/twitter-ads/targeting_criteria/conversation.rb +23 -0
- data/lib/twitter-ads/utils.rb +23 -0
- data/lib/twitter-ads/version.rb +1 -1
- data/spec/fixtures/tweet_previews.json +23 -0
- data/spec/twitter-ads/campaign/targeting_criteria_spec.rb +0 -1
- data/spec/twitter-ads/campaign/tweet_spec.rb +0 -59
- data/spec/twitter-ads/client_spec.rb +17 -1
- data/spec/twitter-ads/creative/tweet_previews_spec.rb +41 -0
- data/spec/twitter-ads/rate_limit_spec.rb +247 -0
- data/spec/twitter-ads/retry_count_spec.rb +61 -0
- metadata +14 -17
- data/lib/twitter-ads/audiences/audience_intelligence.rb +0 -68
- data/spec/fixtures/tweet_preview.json +0 -24
- data/spec/twitter-ads/creative/account_media_spec.rb +0 -32
- data/spec/twitter-ads/creative/image_app_download_card_spec.rb +0 -43
- data/spec/twitter-ads/creative/image_conversation_card_spec.rb +0 -40
- data/spec/twitter-ads/creative/video_app_download_card_spec.rb +0 -42
- data/spec/twitter-ads/creative/video_conversation_card_spec.rb +0 -51
- data/spec/twitter-ads/creative/website_card_spec.rb +0 -42
| @@ -20,7 +20,14 @@ module TwitterAds | |
| 20 20 | 
             
                  # @return [self] A fully hydrated instance of the current class.
         | 
| 21 21 | 
             
                  #
         | 
| 22 22 | 
             
                  # @since 0.1.0
         | 
| 23 | 
            -
                  def from_response(object)
         | 
| 23 | 
            +
                  def from_response(object, headers = nil)
         | 
| 24 | 
            +
                    if !headers.nil?
         | 
| 25 | 
            +
                      TwitterAds::Utils.extract_response_headers(headers).each { |key, value|
         | 
| 26 | 
            +
                        singleton_class.class_eval { attr_accessor key }
         | 
| 27 | 
            +
                        instance_variable_set("@#{key}", value)
         | 
| 28 | 
            +
                      }
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
             | 
| 24 31 | 
             
                    self.class.properties.each do |name, type|
         | 
| 25 32 | 
             
                      value = nil
         | 
| 26 33 | 
             
                      if type == :time && object[name] && !object[name].empty?
         | 
| @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
            # Copyright (C) 2019 Twitter, Inc.
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module TwitterRestApi
         | 
| 5 | 
            +
              class UserIdLookup
         | 
| 6 | 
            +
                include TwitterAds::DSL
         | 
| 7 | 
            +
                include TwitterAds::Resource
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                attr_reader :account
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                property :id, read_only: true
         | 
| 12 | 
            +
                property :id_str, read_only: true
         | 
| 13 | 
            +
                property :screen_name, read_only: true
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                DOMAIN = 'https://api.twitter.com'
         | 
| 16 | 
            +
                RESOURCE = '/1.1/users/show.json'
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def self.load(account, opts = {})
         | 
| 19 | 
            +
                  response = TwitterAds::Request.new(
         | 
| 20 | 
            +
                    account.client,
         | 
| 21 | 
            +
                    :get,
         | 
| 22 | 
            +
                    RESOURCE,
         | 
| 23 | 
            +
                    params: opts,
         | 
| 24 | 
            +
                    domain: DOMAIN
         | 
| 25 | 
            +
                  ).perform
         | 
| 26 | 
            +
                  new.from_response(response.body, response.headers)
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
            end
         | 
| @@ -41,12 +41,24 @@ module TwitterAds | |
| 41 41 | 
             
                property :to_delete, type: :bool
         | 
| 42 42 |  | 
| 43 43 | 
             
                RESOURCE = "/#{TwitterAds::API_VERSION}/" \
         | 
| 44 | 
            -
                           'accounts/%{account_id}/ | 
| 44 | 
            +
                           'accounts/%{account_id}/tax_settings' # @api private
         | 
| 45 45 |  | 
| 46 46 | 
             
                def initialize(account)
         | 
| 47 47 | 
             
                  @account = account
         | 
| 48 48 | 
             
                  self
         | 
| 49 49 | 
             
                end
         | 
| 50 50 |  | 
| 51 | 
            +
                def self.load(account)
         | 
| 52 | 
            +
                  resource = RESOURCE % { account_id: account.id }
         | 
| 53 | 
            +
                  response = Request.new(account.client, :get, resource).perform
         | 
| 54 | 
            +
                  new(account).from_response(response.body[:data])
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                def save
         | 
| 58 | 
            +
                  resource = RESOURCE % { account_id: account.id }
         | 
| 59 | 
            +
                  params = to_params
         | 
| 60 | 
            +
                  response = Request.new(account.client, :put, resource, params: params).perform
         | 
| 61 | 
            +
                  from_response(response.body[:data])
         | 
| 62 | 
            +
                end
         | 
| 51 63 | 
             
              end
         | 
| 52 64 | 
             
            end
         | 
| @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
            # Copyright (C) 2019 Twitter, Inc.
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module TwitterAds
         | 
| 5 | 
            +
              class Conversation
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                include TwitterAds::DSL
         | 
| 8 | 
            +
                include TwitterAds::Resource
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                property :name, read_only: true
         | 
| 11 | 
            +
                property :targeting_type, read_only: true
         | 
| 12 | 
            +
                property :targeting_value, read_only: true
         | 
| 13 | 
            +
                property :conversation_type, read_only: true
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                RESOURCE_COLLECTION = "/#{TwitterAds::API_VERSION}/" \
         | 
| 16 | 
            +
                                      'targeting_criteria/conversations' # @api private
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def initialize(account)
         | 
| 19 | 
            +
                  @account = account
         | 
| 20 | 
            +
                  self
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
            end
         | 
    
        data/lib/twitter-ads/utils.rb
    CHANGED
    
    | @@ -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
         | 
    
        data/lib/twitter-ads/version.rb
    CHANGED
    
    
| @@ -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 | 
            +
            }
         | 
| @@ -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
         | 
| @@ -55,10 +55,26 @@ describe TwitterAds::Client do | |
| 55 55 |  | 
| 56 56 | 
             
                it 'allows additional options' do
         | 
| 57 57 | 
             
                  expect {
         | 
| 58 | 
            -
                    Client.new(consumer_key, consumer_secret, access_token, {})
         | 
| 58 | 
            +
                    Client.new(consumer_key, consumer_secret, access_token, access_token_secret, options: {})
         | 
| 59 59 | 
             
                  }.not_to raise_error
         | 
| 60 60 | 
             
                end
         | 
| 61 61 |  | 
| 62 | 
            +
                it 'test client options' do
         | 
| 63 | 
            +
                  client = Client.new(
         | 
| 64 | 
            +
                    consumer_key,
         | 
| 65 | 
            +
                    consumer_secret,
         | 
| 66 | 
            +
                    access_token,
         | 
| 67 | 
            +
                    access_token_secret,
         | 
| 68 | 
            +
                    options: {
         | 
| 69 | 
            +
                      handle_rate_limit: true,
         | 
| 70 | 
            +
                      retry_max: 1,
         | 
| 71 | 
            +
                      retry_delay: 3000,
         | 
| 72 | 
            +
                      retry_on_status: [404, 500, 503]
         | 
| 73 | 
            +
                    }
         | 
| 74 | 
            +
                  )
         | 
| 75 | 
            +
                  expect(client.options.length).to eq 4
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 62 78 | 
             
              end
         | 
| 63 79 |  | 
| 64 80 | 
             
              describe '#inspect' do
         | 
| @@ -0,0 +1,41 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
            # Copyright (C) 2019 Twitter, Inc.
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require 'spec_helper'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            include TwitterAds::Enum
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            describe TwitterAds::Creative::TweetPreview do
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              let!(:resource) { "#{ADS_API}/accounts/2iqph/tweet_previews" }
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              before(:each) do
         | 
| 13 | 
            +
                stub_fixture(:get, :accounts_load, "#{ADS_API}/accounts/2iqph")
         | 
| 14 | 
            +
                stub_fixture(:get, :tweet_previews, /#{resource}\?.*/)
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              let(:client) do
         | 
| 18 | 
            +
                Client.new(
         | 
| 19 | 
            +
                  Faker::Lorem.characters(40),
         | 
| 20 | 
            +
                  Faker::Lorem.characters(40),
         | 
| 21 | 
            +
                  Faker::Lorem.characters(40),
         | 
| 22 | 
            +
                  Faker::Lorem.characters(40)
         | 
| 23 | 
            +
                )
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              let(:account) { client.accounts('2iqph') }
         | 
| 27 | 
            +
              let(:instance) { described_class.new(account) }
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              it 'inspect TweetPreview.load() response' do
         | 
| 30 | 
            +
                preview = instance.load(
         | 
| 31 | 
            +
                  account,
         | 
| 32 | 
            +
                  tweet_ids: %w(1130942781109596160 1101254234031370240),
         | 
| 33 | 
            +
                  tweet_type: TweetType::PUBLISHED)
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                expect(preview).to be_instance_of(Cursor)
         | 
| 36 | 
            +
                expect(preview.count).to eq 2
         | 
| 37 | 
            +
                tweet = preview.first
         | 
| 38 | 
            +
                expect(tweet.tweet_id).to eq '1130942781109596160'
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            end
         | 
| @@ -0,0 +1,247 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
            # Copyright (C) 2019 Twitter, Inc.
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require 'spec_helper'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            describe TwitterAds::Campaign do
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              before(:each) do
         | 
| 9 | 
            +
                allow_any_instance_of(Object).to receive(:sleep)
         | 
| 10 | 
            +
                stub_fixture(:get, :accounts_load, "#{ADS_API}/accounts/2iqph")
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              let(:client) do
         | 
| 14 | 
            +
                Client.new(
         | 
| 15 | 
            +
                  Faker::Lorem.characters(40),
         | 
| 16 | 
            +
                  Faker::Lorem.characters(40),
         | 
| 17 | 
            +
                  Faker::Lorem.characters(40),
         | 
| 18 | 
            +
                  Faker::Lorem.characters(40)
         | 
| 19 | 
            +
                )
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
              let(:account) { client_simple.accounts('2iqph') }
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              let(:client_simple) do
         | 
| 24 | 
            +
                Client.new(
         | 
| 25 | 
            +
                  Faker::Lorem.characters(40),
         | 
| 26 | 
            +
                  Faker::Lorem.characters(40),
         | 
| 27 | 
            +
                  Faker::Lorem.characters(40),
         | 
| 28 | 
            +
                  Faker::Lorem.characters(40),
         | 
| 29 | 
            +
                  options: {
         | 
| 30 | 
            +
                    handle_rate_limit: true
         | 
| 31 | 
            +
                  }
         | 
| 32 | 
            +
                )
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
              let(:account1) { client_simple.accounts('2iqph') }
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              let(:client_combine) do
         | 
| 37 | 
            +
                Client.new(
         | 
| 38 | 
            +
                  Faker::Lorem.characters(40),
         | 
| 39 | 
            +
                  Faker::Lorem.characters(40),
         | 
| 40 | 
            +
                  Faker::Lorem.characters(40),
         | 
| 41 | 
            +
                  Faker::Lorem.characters(40),
         | 
| 42 | 
            +
                  options: {
         | 
| 43 | 
            +
                    handle_rate_limit: true,
         | 
| 44 | 
            +
                    retry_max: 1,
         | 
| 45 | 
            +
                    retry_delay: 3000,
         | 
| 46 | 
            +
                    retry_on_status: [500]
         | 
| 47 | 
            +
                  }
         | 
| 48 | 
            +
                )
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
              let(:account2) { client_combine.accounts('2iqph') }
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              it 'test rate-limit handle - success' do
         | 
| 53 | 
            +
                stub = stub_request(:get, "#{ADS_API}/accounts/2iqph/campaigns").to_return(
         | 
| 54 | 
            +
                  {
         | 
| 55 | 
            +
                    body: fixture(:campaigns_all),
         | 
| 56 | 
            +
                    status: 429,
         | 
| 57 | 
            +
                    headers: {
         | 
| 58 | 
            +
                      'x-account-rate-limit-limit': 10000,
         | 
| 59 | 
            +
                      'x-account-rate-limit-remaining': 9999,
         | 
| 60 | 
            +
                      'x-account-rate-limit-reset': Time.now.to_i + 5
         | 
| 61 | 
            +
                    }
         | 
| 62 | 
            +
                  },
         | 
| 63 | 
            +
                  {
         | 
| 64 | 
            +
                    body: fixture(:campaigns_all),
         | 
| 65 | 
            +
                    status: 200
         | 
| 66 | 
            +
                  }
         | 
| 67 | 
            +
                )
         | 
| 68 | 
            +
                cusor = described_class.all(account1)
         | 
| 69 | 
            +
                expect(cusor).to be_instance_of(Cursor)
         | 
| 70 | 
            +
                expect(stub).to have_been_requested.times(2)
         | 
| 71 | 
            +
              end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
              it 'test rate-limit handle - fail' do
         | 
| 74 | 
            +
                stub = stub_request(:get, "#{ADS_API}/accounts/2iqph/campaigns").to_return(
         | 
| 75 | 
            +
                  {
         | 
| 76 | 
            +
                    body: fixture(:campaigns_all),
         | 
| 77 | 
            +
                    status: 429,
         | 
| 78 | 
            +
                    headers: {
         | 
| 79 | 
            +
                      'x-account-rate-limit-limit': 10000,
         | 
| 80 | 
            +
                      'x-account-rate-limit-remaining': 9999,
         | 
| 81 | 
            +
                      'x-account-rate-limit-reset': Time.now.to_i + 5
         | 
| 82 | 
            +
                    }
         | 
| 83 | 
            +
                  },
         | 
| 84 | 
            +
                  {
         | 
| 85 | 
            +
                    body: fixture(:campaigns_all),
         | 
| 86 | 
            +
                    status: 429,
         | 
| 87 | 
            +
                    headers: {
         | 
| 88 | 
            +
                      'x-account-rate-limit-limit': 10000,
         | 
| 89 | 
            +
                      'x-account-rate-limit-remaining': 9999,
         | 
| 90 | 
            +
                      'x-account-rate-limit-reset': 4102444800
         | 
| 91 | 
            +
                    }
         | 
| 92 | 
            +
                  }
         | 
| 93 | 
            +
                )
         | 
| 94 | 
            +
                begin
         | 
| 95 | 
            +
                  cusor = described_class.all(account1)
         | 
| 96 | 
            +
                rescue RateLimit => e
         | 
| 97 | 
            +
                  cusor = e
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
                expect(cusor).to be_instance_of(RateLimit)
         | 
| 100 | 
            +
                expect(stub).to have_been_requested.times(2)
         | 
| 101 | 
            +
                expect(cusor.reset_at).to eq 4102444800
         | 
| 102 | 
            +
              end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
              it 'test rate-limit handle with retry - case 1' do
         | 
| 105 | 
            +
                # scenario:
         | 
| 106 | 
            +
                #  - 500 (retry) -> 429 (handle rate limit) -> 200 (end)
         | 
| 107 | 
            +
                stub = stub_request(:get, "#{ADS_API}/accounts/2iqph/campaigns").to_return(
         | 
| 108 | 
            +
                  {
         | 
| 109 | 
            +
                    body: fixture(:campaigns_all),
         | 
| 110 | 
            +
                    status: 500,
         | 
| 111 | 
            +
                    headers: {
         | 
| 112 | 
            +
                      'x-account-rate-limit-limit': 10000,
         | 
| 113 | 
            +
                      'x-account-rate-limit-remaining': 9999,
         | 
| 114 | 
            +
                      'x-account-rate-limit-reset': 4102444800
         | 
| 115 | 
            +
                    }
         | 
| 116 | 
            +
                  },
         | 
| 117 | 
            +
                  {
         | 
| 118 | 
            +
                    body: fixture(:campaigns_all),
         | 
| 119 | 
            +
                    status: 429,
         | 
| 120 | 
            +
                    headers: {
         | 
| 121 | 
            +
                      'x-account-rate-limit-limit': 10000,
         | 
| 122 | 
            +
                      'x-account-rate-limit-remaining': 9999,
         | 
| 123 | 
            +
                      'x-account-rate-limit-reset': Time.now.to_i + 5
         | 
| 124 | 
            +
                    }
         | 
| 125 | 
            +
                  },
         | 
| 126 | 
            +
                  {
         | 
| 127 | 
            +
                    body: fixture(:campaigns_all),
         | 
| 128 | 
            +
                    status: 200
         | 
| 129 | 
            +
                  }
         | 
| 130 | 
            +
                )
         | 
| 131 | 
            +
                cusor = described_class.all(account2)
         | 
| 132 | 
            +
                expect(cusor).to be_instance_of(Cursor)
         | 
| 133 | 
            +
                expect(stub).to have_been_requested.times(3)
         | 
| 134 | 
            +
              end
         | 
| 135 | 
            +
             | 
| 136 | 
            +
              it 'test rate-limit handle with retry - case 2' do
         | 
| 137 | 
            +
                # scenario:
         | 
| 138 | 
            +
                #  - 429 (handle rate limit) -> 500 (retry) -> 200 (end)
         | 
| 139 | 
            +
                stub = stub_request(:get, "#{ADS_API}/accounts/2iqph/campaigns").to_return(
         | 
| 140 | 
            +
                  {
         | 
| 141 | 
            +
                    body: fixture(:campaigns_all),
         | 
| 142 | 
            +
                    status: 429,
         | 
| 143 | 
            +
                    headers: {
         | 
| 144 | 
            +
                      'x-account-rate-limit-limit': 10000,
         | 
| 145 | 
            +
                      'x-account-rate-limit-remaining': 9999,
         | 
| 146 | 
            +
                      'x-account-rate-limit-reset': Time.now.to_i + 5
         | 
| 147 | 
            +
                    }
         | 
| 148 | 
            +
                  },
         | 
| 149 | 
            +
                  {
         | 
| 150 | 
            +
                    body: fixture(:campaigns_all),
         | 
| 151 | 
            +
                    status: 500,
         | 
| 152 | 
            +
                    headers: {
         | 
| 153 | 
            +
                      'x-account-rate-limit-limit': 10000,
         | 
| 154 | 
            +
                      'x-account-rate-limit-remaining': 9999,
         | 
| 155 | 
            +
                      'x-account-rate-limit-reset': 4102444800
         | 
| 156 | 
            +
                    }
         | 
| 157 | 
            +
                  },
         | 
| 158 | 
            +
                  {
         | 
| 159 | 
            +
                    body: fixture(:campaigns_all),
         | 
| 160 | 
            +
                    status: 200
         | 
| 161 | 
            +
                  }
         | 
| 162 | 
            +
                )
         | 
| 163 | 
            +
                cusor = described_class.all(account2)
         | 
| 164 | 
            +
                expect(cusor).to be_instance_of(Cursor)
         | 
| 165 | 
            +
                expect(stub).to have_been_requested.times(3)
         | 
| 166 | 
            +
              end
         | 
| 167 | 
            +
             | 
| 168 | 
            +
              it 'test rate-limit header access from cusor class' do
         | 
| 169 | 
            +
                stub_request(:get, "#{ADS_API}/accounts/2iqph/campaigns").to_return(
         | 
| 170 | 
            +
                  {
         | 
| 171 | 
            +
                    body: fixture(:campaigns_all),
         | 
| 172 | 
            +
                    status: 200,
         | 
| 173 | 
            +
                    headers: {
         | 
| 174 | 
            +
                      'x-account-rate-limit-limit': 10000,
         | 
| 175 | 
            +
                      'x-account-rate-limit-remaining': 9999,
         | 
| 176 | 
            +
                      'x-account-rate-limit-reset': 4102444800
         | 
| 177 | 
            +
                    }
         | 
| 178 | 
            +
                  }
         | 
| 179 | 
            +
                )
         | 
| 180 | 
            +
                cusor = described_class.all(account)
         | 
| 181 | 
            +
                expect(cusor).to be_instance_of(Cursor)
         | 
| 182 | 
            +
                expect(cusor.account_rate_limit_limit).to eq 10000
         | 
| 183 | 
            +
                expect(cusor.account_rate_limit_remaining).to eq 9999
         | 
| 184 | 
            +
                expect(cusor.account_rate_limit_reset).to eq 4102444800
         | 
| 185 | 
            +
              end
         | 
| 186 | 
            +
             | 
| 187 | 
            +
              it 'test rate-limit header access from resource class' do
         | 
| 188 | 
            +
                stub_request(:any, /accounts\/2iqph\/campaigns\/2wap7/).to_return(
         | 
| 189 | 
            +
                  {
         | 
| 190 | 
            +
                    body: fixture(:campaigns_load),
         | 
| 191 | 
            +
                    status: 200,
         | 
| 192 | 
            +
                    headers: {
         | 
| 193 | 
            +
                      'x-account-rate-limit-limit': 10000,
         | 
| 194 | 
            +
                      'x-account-rate-limit-remaining': 9999,
         | 
| 195 | 
            +
                      'x-account-rate-limit-reset': 4102444800
         | 
| 196 | 
            +
                    }
         | 
| 197 | 
            +
                  }
         | 
| 198 | 
            +
                )
         | 
| 199 | 
            +
             | 
| 200 | 
            +
                campaign = described_class.load(account, '2wap7')
         | 
| 201 | 
            +
                resource = "/#{TwitterAds::API_VERSION}/accounts/2iqph/campaigns/2wap7"
         | 
| 202 | 
            +
                params   = {}
         | 
| 203 | 
            +
                response = TwitterAds::Request.new(client, :get, resource, params: params).perform
         | 
| 204 | 
            +
                data     = campaign.from_response(response.body[:data], response.headers)
         | 
| 205 | 
            +
                expect(data).to be_instance_of(Campaign)
         | 
| 206 | 
            +
                expect(data.account_rate_limit_limit).to eq 10000
         | 
| 207 | 
            +
                expect(data.account_rate_limit_remaining).to eq 9999
         | 
| 208 | 
            +
                expect(data.account_rate_limit_reset).to eq 4102444800
         | 
| 209 | 
            +
              end
         | 
| 210 | 
            +
             | 
| 211 | 
            +
              it 'test rate-limit header access check instance variables not conflicting' do
         | 
| 212 | 
            +
                stub_request(:get, "#{ADS_API}/accounts/2iqph/campaigns").to_return(
         | 
| 213 | 
            +
                  {
         | 
| 214 | 
            +
                    body: fixture(:campaigns_all),
         | 
| 215 | 
            +
                    status: 200,
         | 
| 216 | 
            +
                    headers: {
         | 
| 217 | 
            +
                      'x-account-rate-limit-limit': 10000,
         | 
| 218 | 
            +
                      'x-account-rate-limit-remaining': 9999,
         | 
| 219 | 
            +
                      'x-account-rate-limit-reset': 4102444800
         | 
| 220 | 
            +
                    }
         | 
| 221 | 
            +
                  },
         | 
| 222 | 
            +
                  {
         | 
| 223 | 
            +
                    body: fixture(:campaigns_all),
         | 
| 224 | 
            +
                    status: 200,
         | 
| 225 | 
            +
                    headers: {
         | 
| 226 | 
            +
                      'x-account-rate-limit-limit': 10000,
         | 
| 227 | 
            +
                      'x-account-rate-limit-remaining': 9998,
         | 
| 228 | 
            +
                      'x-account-rate-limit-reset': 4102444800
         | 
| 229 | 
            +
                    }
         | 
| 230 | 
            +
                  }
         | 
| 231 | 
            +
                )
         | 
| 232 | 
            +
             | 
| 233 | 
            +
                campaign_first = described_class.all(account)
         | 
| 234 | 
            +
                campaign_second = described_class.all(account)
         | 
| 235 | 
            +
             | 
| 236 | 
            +
                expect(campaign_first).to be_instance_of(Cursor)
         | 
| 237 | 
            +
                expect(campaign_first.account_rate_limit_limit).to eq 10000
         | 
| 238 | 
            +
                expect(campaign_first.account_rate_limit_remaining).to eq 9999
         | 
| 239 | 
            +
                expect(campaign_first.account_rate_limit_reset).to eq 4102444800
         | 
| 240 | 
            +
             | 
| 241 | 
            +
                expect(campaign_second).to be_instance_of(Cursor)
         | 
| 242 | 
            +
                expect(campaign_second.account_rate_limit_limit).to eq 10000
         | 
| 243 | 
            +
                expect(campaign_second.account_rate_limit_remaining).to eq 9998
         | 
| 244 | 
            +
                expect(campaign_second.account_rate_limit_reset).to eq 4102444800
         | 
| 245 | 
            +
              end
         | 
| 246 | 
            +
             | 
| 247 | 
            +
            end
         |