From d7a99e37403857ee274e44ee986e7c2eaad57f78 Mon Sep 17 00:00:00 2001 From: Shohei Maeda Date: Thu, 19 Sep 2019 06:16:28 +0900 Subject: [PATCH] v6 updates (#210) * Update API_VERSION * v6: Expose concurrent job limit - Analytics module is now Class object * Add support for PROMOTED_ACCOUNT stats entity * mark scoped_timeline as deprecated * as_user_id is now required parameter * Add additional optimization enum * remove tailored_audience_type request parameter * rename lookalike_expansion to audience_expansion * Media identifier consistency * remove meaningless tests * bump version * Fix comments in Analytics class * Add helper method * Add new Tweets endpoint * Update comments --- examples/draft_tweet.rb | 6 +- examples/promoted_tweet.rb | 8 +- lib/twitter-ads.rb | 3 + lib/twitter-ads/account.rb | 17 +-- lib/twitter-ads/campaign/campaign.rb | 3 +- .../campaign/funding_instrument.rb | 3 +- lib/twitter-ads/campaign/line_item.rb | 5 +- lib/twitter-ads/campaign/organic_tweet.rb | 4 +- .../campaign/targeting_criteria.rb | 1 - lib/twitter-ads/campaign/tweet.rb | 8 +- lib/twitter-ads/client.rb | 2 +- lib/twitter-ads/creative/account_media.rb | 10 +- lib/twitter-ads/creative/draft_tweet.rb | 3 +- .../creative/image_app_download_card.rb | 4 +- .../creative/image_conversation_card.rb | 5 +- lib/twitter-ads/creative/media_creative.rb | 3 +- lib/twitter-ads/creative/media_library.rb | 6 +- lib/twitter-ads/creative/promoted_account.rb | 3 +- lib/twitter-ads/creative/promoted_tweet.rb | 3 +- lib/twitter-ads/creative/scheduled_tweet.rb | 3 +- lib/twitter-ads/creative/tweets.rb | 52 +++++++ .../creative/video_app_download_card.rb | 10 +- .../creative/video_conversation_card.rb | 12 +- .../creative/video_website_card.rb | 8 +- lib/twitter-ads/creative/website_card.rb | 4 +- lib/twitter-ads/enum.rb | 10 ++ lib/twitter-ads/resources/analytics.rb | 136 ++++++++++++------ lib/twitter-ads/restapi.rb | 29 ++++ lib/twitter-ads/utils.rb | 11 ++ lib/twitter-ads/version.rb | 2 +- .../campaign/targeting_criteria_spec.rb | 1 - .../creative/account_media_spec.rb | 32 ----- .../creative/image_app_download_card_spec.rb | 43 ------ .../creative/image_conversation_card_spec.rb | 40 ------ .../creative/video_app_download_card_spec.rb | 42 ------ .../creative/video_conversation_card_spec.rb | 51 ------- .../twitter-ads/creative/website_card_spec.rb | 42 ------ 37 files changed, 255 insertions(+), 370 deletions(-) create mode 100644 lib/twitter-ads/creative/tweets.rb create mode 100644 lib/twitter-ads/restapi.rb delete mode 100644 spec/twitter-ads/creative/account_media_spec.rb delete mode 100644 spec/twitter-ads/creative/image_app_download_card_spec.rb delete mode 100644 spec/twitter-ads/creative/image_conversation_card_spec.rb delete mode 100644 spec/twitter-ads/creative/video_app_download_card_spec.rb delete mode 100644 spec/twitter-ads/creative/video_conversation_card_spec.rb delete mode 100644 spec/twitter-ads/creative/website_card_spec.rb diff --git a/examples/draft_tweet.rb b/examples/draft_tweet.rb index 840307f8..71f1f9b2 100644 --- a/examples/draft_tweet.rb +++ b/examples/draft_tweet.rb @@ -20,6 +20,9 @@ # load up the account instance, campaign and line item account = client.accounts(ADS_ACCOUNT) +# get user_id for as_user_id parameter +user_id = TwitterRestApi::UserIdLookup.load(account, screen_name: 'your_twitter_handle_name').id + # fetch draft tweets from a given account tweets = TwitterAds::Creative::DraftTweet.all(account) tweets.each { |tweet| @@ -30,6 +33,7 @@ # create a new draft tweet draft_tweet = TwitterAds::Creative::DraftTweet.new(account) draft_tweet.text = 'draft tweet - new' +draft_tweet.as_user_id = user_id draft_tweet.save p draft_tweet.id_str p draft_tweet.text @@ -52,7 +56,7 @@ # draft_tweet.preview(draft_tweet_id: '1142048306194862080') # create a nullcasted tweet using draft tweet metadata -tweet = TwitterAds::Tweet.create(account, text: draft_tweet.text) +tweet = TwitterAds::Tweet.create(account, text: draft_tweet.text, as_user_id: user_id) p tweet # delete draft tweet diff --git a/examples/promoted_tweet.rb b/examples/promoted_tweet.rb index c0225b9b..b80f8848 100644 --- a/examples/promoted_tweet.rb +++ b/examples/promoted_tweet.rb @@ -22,8 +22,11 @@ campaign = account.campaigns.first line_item = account.line_items(nil, campaign_ids: campaign.id).first +# get user_id for as_user_id parameter +user_id = TwitterRestApi::UserIdLookup.load(account, screen_name: 'your_twitter_handle_name').id + # create request for a simple nullcasted tweet -tweet1 = TwitterAds::Tweet.create(account, text: 'There can be only one...') +tweet1 = TwitterAds::Tweet.create(account, text: 'There can be only one...', as_user_id: user_id) # promote the tweet using our line item promoted_tweet = TwitterAds::Creative::PromotedTweet.new(account) @@ -36,7 +39,8 @@ tweet2 = TwitterAds::Tweet.create( account, text: 'Fine. There can be two.', - card_uri: website_card.card_uri + card_uri: website_card.card_uri, + as_user_id: user_id ) # promote the tweet using our line item diff --git a/lib/twitter-ads.rb b/lib/twitter-ads.rb index 30c7f863..9784d091 100644 --- a/lib/twitter-ads.rb +++ b/lib/twitter-ads.rb @@ -27,6 +27,8 @@ require 'twitter-ads/http/request' require 'twitter-ads/http/response' +require 'twitter-ads/restapi.rb' + require 'twitter-ads/audiences/tailored_audience' require 'twitter-ads/campaign/app_list' @@ -69,6 +71,7 @@ require 'twitter-ads/creative/website_card' require 'twitter-ads/creative/poll_cards' require 'twitter-ads/creative/tweet_previews' +require 'twitter-ads/creative/tweets' require 'twitter-ads/targeting/reach_estimate' diff --git a/lib/twitter-ads/account.rb b/lib/twitter-ads/account.rb index cf5e56f0..73c4cbe5 100644 --- a/lib/twitter-ads/account.rb +++ b/lib/twitter-ads/account.rb @@ -17,14 +17,13 @@ class Account property :updated_at, type: :time, read_only: true property :deleted, type: :bool, read_only: true - RESOURCE_COLLECTION = "/#{TwitterAds::API_VERSION}/" \ - 'accounts' # @api private - RESOURCE = "/#{TwitterAds::API_VERSION}/" \ - 'accounts/%{id}' # @api private - FEATURES = "/#{TwitterAds::API_VERSION}/" \ - 'accounts/%{id}/features' # @api private - SCOPED_TIMELINE = "/#{TwitterAds::API_VERSION}/" \ - 'accounts/%{id}/scoped_timeline' # @api private + RESOURCE_COLLECTION = "/#{TwitterAds::API_VERSION}/" \ + 'accounts' # @api private + RESOURCE = "/#{TwitterAds::API_VERSION}/" \ + 'accounts/%{id}' # @api private + FEATURES = "/#{TwitterAds::API_VERSION}/" \ + 'accounts/%{id}/features' # @api private + SCOPED_TIMELINE = '/5/accounts/%{id}/scoped_timeline' # @api private AUTHENTICATED_USER_ACCESS = "/#{TwitterAds::API_VERSION}/" \ 'accounts/%{id}/authenticated_user_access' # @api private @@ -257,6 +256,8 @@ def tailored_audiences(id = nil, opts = {}) # # @since 0.2.3 def scoped_timeline(id, opts = {}) + TwitterAds::Utils.deprecated( + 'Scoped Timeline') params = { user_id: id }.merge!(opts) resource = SCOPED_TIMELINE % { id: @id } request = Request.new(client, :get, resource, params: params) diff --git a/lib/twitter-ads/campaign/campaign.rb b/lib/twitter-ads/campaign/campaign.rb index 5c355030..40ff73ee 100644 --- a/lib/twitter-ads/campaign/campaign.rb +++ b/lib/twitter-ads/campaign/campaign.rb @@ -2,12 +2,11 @@ # Copyright (C) 2019 Twitter, Inc. module TwitterAds - class Campaign + class Campaign < Analytics include TwitterAds::DSL include TwitterAds::Resource include TwitterAds::Persistence - include TwitterAds::Analytics include TwitterAds::Batch attr_reader :account diff --git a/lib/twitter-ads/campaign/funding_instrument.rb b/lib/twitter-ads/campaign/funding_instrument.rb index ef898bff..e4e6ff30 100644 --- a/lib/twitter-ads/campaign/funding_instrument.rb +++ b/lib/twitter-ads/campaign/funding_instrument.rb @@ -2,11 +2,10 @@ # Copyright (C) 2019 Twitter, Inc. module TwitterAds - class FundingInstrument + class FundingInstrument < Analytics include TwitterAds::DSL include TwitterAds::Resource - include TwitterAds::Analytics attr_reader :account diff --git a/lib/twitter-ads/campaign/line_item.rb b/lib/twitter-ads/campaign/line_item.rb index a303c4e4..2ad84645 100644 --- a/lib/twitter-ads/campaign/line_item.rb +++ b/lib/twitter-ads/campaign/line_item.rb @@ -2,12 +2,11 @@ # Copyright (C) 2019 Twitter, Inc. module TwitterAds - class LineItem + class LineItem < Analytics include TwitterAds::DSL include TwitterAds::Resource include TwitterAds::Persistence - include TwitterAds::Analytics include TwitterAds::Batch attr_reader :account @@ -40,7 +39,7 @@ class LineItem property :advertiser_user_id property :bid_type property :tracking_tags - property :lookalike_expansion + property :audience_expansion # sdk only property :to_delete, type: :bool diff --git a/lib/twitter-ads/campaign/organic_tweet.rb b/lib/twitter-ads/campaign/organic_tweet.rb index 69720998..c2fe29df 100644 --- a/lib/twitter-ads/campaign/organic_tweet.rb +++ b/lib/twitter-ads/campaign/organic_tweet.rb @@ -3,9 +3,7 @@ # Author Bob, Nugit module TwitterAds - class OrganicTweet - - include TwitterAds::Analytics + class OrganicTweet < Analytics end end diff --git a/lib/twitter-ads/campaign/targeting_criteria.rb b/lib/twitter-ads/campaign/targeting_criteria.rb index 0ae81b23..16f9bce9 100644 --- a/lib/twitter-ads/campaign/targeting_criteria.rb +++ b/lib/twitter-ads/campaign/targeting_criteria.rb @@ -22,7 +22,6 @@ class TargetingCriteria property :targeting_type property :targeting_value property :tailored_audience_expansion, type: :bool - property :tailored_audience_type property :location_type # sdk only diff --git a/lib/twitter-ads/campaign/tweet.rb b/lib/twitter-ads/campaign/tweet.rb index cf496ba3..831c04b9 100644 --- a/lib/twitter-ads/campaign/tweet.rb +++ b/lib/twitter-ads/campaign/tweet.rb @@ -2,10 +2,7 @@ # Copyright (C) 2019 Twitter, Inc. module TwitterAds - module Tweet - - # cannot instaniate Tweet, only including class methods for stats - extend TwitterAds::Analytics::ClassMethods + class Tweet < Analytics RESOURCE_CREATE = "/#{TwitterAds::API_VERSION}/" \ 'accounts/%{account_id}/tweet' # @api private @@ -17,10 +14,9 @@ class << self # @param opts [Hash] A hash of options. # # @option opts [String] :text The main Tweet body. - # @option opts [Array] :media_ids A list of up to four media IDs to associate with the Tweet. + # @option opts [Array] :media_keys A list of media keys (up to 4) to associate with the Tweet. # @option opts [Integer] :as_user_id The user ID whom you are posting the Tweet on behalf of. # @option opts [Boolean] :trim_user Excludes the user object from the hydrated Tweet response. - # @option opts [String] :video_id The Video UUID to be associated with thie Tweet. # @option opts [String] :video_title An optional title to be included. # @option opts [String] :video_description An optional description to be included. # @option opts [String] :video_cta An optional CTA value for the associated video. diff --git a/lib/twitter-ads/client.rb b/lib/twitter-ads/client.rb index 1dbc7a59..341a482b 100644 --- a/lib/twitter-ads/client.rb +++ b/lib/twitter-ads/client.rb @@ -3,7 +3,7 @@ module TwitterAds - API_VERSION = '5' + API_VERSION = '6' # The Ads API Client class which functions as a # container for basic API consumer information. diff --git a/lib/twitter-ads/creative/account_media.rb b/lib/twitter-ads/creative/account_media.rb index bce000e6..d338b276 100644 --- a/lib/twitter-ads/creative/account_media.rb +++ b/lib/twitter-ads/creative/account_media.rb @@ -12,15 +12,13 @@ class AccountMedia attr_reader :account - property :created_at, type: :time, read_only: true property :deleted, type: :bool, read_only: true + property :created_at, type: :time, read_only: true + property :updated_at, type: :time, read_only: true property :id, read_only: true + property :creative_type, read_only: true + property :media_key, read_only: true property :media_url, read_only: true - property :updated_at, type: :time, read_only: true - - property :creative_type - property :media_id - property :video_id RESOURCE_COLLECTION = "/#{TwitterAds::API_VERSION}/" \ 'accounts/%{account_id}/account_media' # @api private diff --git a/lib/twitter-ads/creative/draft_tweet.rb b/lib/twitter-ads/creative/draft_tweet.rb index 052735cb..e45ff9f2 100644 --- a/lib/twitter-ads/creative/draft_tweet.rb +++ b/lib/twitter-ads/creative/draft_tweet.rb @@ -15,14 +15,13 @@ class DraftTweet # read-only property :id, read_only: true property :id_str, read_only: true - property :media_keys, read_only: true property :created_at, type: :time, read_only: true property :updated_at, type: :time, read_only: true property :user_id, read_only: true # writable property :as_user_id property :card_uri - property :media_ids + property :media_keys property :nullcast, type: :bool property :text diff --git a/lib/twitter-ads/creative/image_app_download_card.rb b/lib/twitter-ads/creative/image_app_download_card.rb index 7973dda2..58d6a43e 100644 --- a/lib/twitter-ads/creative/image_app_download_card.rb +++ b/lib/twitter-ads/creative/image_app_download_card.rb @@ -20,7 +20,7 @@ class ImageAppDownloadCard property :image_display_height, read_only: true property :image_display_width, read_only: true property :updated_at, type: :time, read_only: true - property :wide_app_image, read_only: true + property :media_url, read_only: true property :country_code property :app_cta @@ -31,7 +31,7 @@ class ImageAppDownloadCard property :ipad_app_id property :ipad_deep_link property :name - property :wide_app_image_media_id + property :media_key RESOURCE_COLLECTION = "/#{TwitterAds::API_VERSION}/" \ 'accounts/%{account_id}/cards/image_app_download' # @api private diff --git a/lib/twitter-ads/creative/image_conversation_card.rb b/lib/twitter-ads/creative/image_conversation_card.rb index 6c22925d..0382836e 100644 --- a/lib/twitter-ads/creative/image_conversation_card.rb +++ b/lib/twitter-ads/creative/image_conversation_card.rb @@ -19,13 +19,14 @@ class ImageConversationCard property :id, read_only: true property :image, read_only: true property :updated_at, type: :time, read_only: true + property :media_url, read_only: true - property :cover_image_id + property :unlocked_image_media_key property :first_cta property :first_cta_tweet property :fourth_cta property :fourth_cta_tweet - property :image_media_id + property :media_key property :name property :second_cta property :second_cta_tweet diff --git a/lib/twitter-ads/creative/media_creative.rb b/lib/twitter-ads/creative/media_creative.rb index 07488585..08b6c2d1 100644 --- a/lib/twitter-ads/creative/media_creative.rb +++ b/lib/twitter-ads/creative/media_creative.rb @@ -4,12 +4,11 @@ module TwitterAds module Creative - class MediaCreative + class MediaCreative < Analytics include TwitterAds::DSL include TwitterAds::Resource include TwitterAds::Persistence - include TwitterAds::Analytics attr_reader :account diff --git a/lib/twitter-ads/creative/media_library.rb b/lib/twitter-ads/creative/media_library.rb index 1408b5ed..f980219d 100644 --- a/lib/twitter-ads/creative/media_library.rb +++ b/lib/twitter-ads/creative/media_library.rb @@ -22,17 +22,15 @@ class MediaLibrary property :media_url, read_only: true property :tweeted, type: :bool, read_only: true property :updated_at, type: :time, read_only: true - property :poster_image_url, read_only: true + property :poster_media_url, read_only: true # writable - property :media_category - property :media_id property :media_key property :description property :file_name property :name property :poster_image_media_id - property :poster_image_media_key + property :poster_media_key property :title RESOURCE_COLLECTION = "/#{TwitterAds::API_VERSION}/" \ diff --git a/lib/twitter-ads/creative/promoted_account.rb b/lib/twitter-ads/creative/promoted_account.rb index 2ff1c232..5c11d500 100644 --- a/lib/twitter-ads/creative/promoted_account.rb +++ b/lib/twitter-ads/creative/promoted_account.rb @@ -4,12 +4,11 @@ module TwitterAds module Creative - class PromotedAccount + class PromotedAccount < Analytics include TwitterAds::DSL include TwitterAds::Resource include TwitterAds::Persistence - include TwitterAds::Analytics attr_reader :account diff --git a/lib/twitter-ads/creative/promoted_tweet.rb b/lib/twitter-ads/creative/promoted_tweet.rb index 6d048d59..b7ec17d7 100644 --- a/lib/twitter-ads/creative/promoted_tweet.rb +++ b/lib/twitter-ads/creative/promoted_tweet.rb @@ -4,12 +4,11 @@ module TwitterAds module Creative - class PromotedTweet + class PromotedTweet < Analytics include TwitterAds::DSL include TwitterAds::Resource include TwitterAds::Persistence - include TwitterAds::Analytics attr_reader :account diff --git a/lib/twitter-ads/creative/scheduled_tweet.rb b/lib/twitter-ads/creative/scheduled_tweet.rb index 4f99586e..55dcb160 100644 --- a/lib/twitter-ads/creative/scheduled_tweet.rb +++ b/lib/twitter-ads/creative/scheduled_tweet.rb @@ -17,7 +17,6 @@ class ScheduledTweet property :created_at, type: :time, read_only: true property :id, read_only: true property :id_str, read_only: true - property :media_keys, read_only: true property :scheduled_status, read_only: true property :tweet_id, read_only: true property :updated_at, type: :time, read_only: true @@ -26,7 +25,7 @@ class ScheduledTweet # writable property :as_user_id property :card_uri - property :media_ids + property :media_keys property :nullcast, type: :bool property :scheduled_at, type: :time property :text diff --git a/lib/twitter-ads/creative/tweets.rb b/lib/twitter-ads/creative/tweets.rb new file mode 100644 index 00000000..ef367ff6 --- /dev/null +++ b/lib/twitter-ads/creative/tweets.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true +# Copyright (C) 2019 Twitter, Inc. + +module TwitterAds + module Creative + + class Tweets + + include TwitterAds::DSL + include TwitterAds::Resource + + attr_reader :account + + RESOURCE_COLLECTION = "/#{TwitterAds::API_VERSION}/" + + 'accounts/%{account_id}/tweets' # @api private + + # Retrieve Tweet details for the account's full promotable user (default) + # or the user specified in the user_id parameter. + # + # @example + # tweets = TwitterAds::Creative::Tweets.all( + # account, + # tweet_type: 'PUBLISHED', + # tweet_ids: %w(1122911801354510336 1102836745790316550), + # timeline_type: 'ORGANIC' + # ) + # + # @param account [Account] The Account object instance. + # @param tweet_type [String] The Tweet type for the specified tweet_ids. + # @option opts [Int] :count The number of records to try and retrieve per distinct request. + # @option opts [String] :cursor A cursor to get the next page of results. + # @option opts [String] :timeline_type The granularity to use (default: NULLCAST). + # @option opts [Boolean] :trim_user Whether to exclude the user object + # in the Tweet response (default: false). + # @option opts [Array] :tweet_ids A collection of tweet IDs to be fetched. + # @option opts [Long] :user_id The user ID to scope Tweets to. + # + # @return A list of tweets details. + # + # @see https://developer.twitter.com/en/docs/ads/creatives/api-reference/tweets#get-accounts-account-id-tweets + # @since 6.0.0 + + def self.all(account, opts = {}) + params = TwitterAds::Utils.flatten_params(opts) + resource = self::RESOURCE_COLLECTION % { account_id: account.id } + request = Request.new(account.client, :get, resource, params: params) + Cursor.new(nil, request, init_with: [account]) + end + + end + end +end diff --git a/lib/twitter-ads/creative/video_app_download_card.rb b/lib/twitter-ads/creative/video_app_download_card.rb index 067f24b9..cadea730 100644 --- a/lib/twitter-ads/creative/video_app_download_card.rb +++ b/lib/twitter-ads/creative/video_app_download_card.rb @@ -18,15 +18,13 @@ class VideoAppDownloadCard property :deleted, type: :bool, read_only: true property :id, read_only: true property :updated_at, type: :time, read_only: true - property :video_content_id, read_only: true - property :video_hls_url, read_only: true property :video_owner_id, read_only: true - property :video_poster_url, read_only: true - property :video_url, read_only: true + property :poster_media_url, read_only: true + property :media_url, read_only: true property :country_code property :app_cta - property :image_media_id + property :poster_media_key property :ipad_app_id property :ipad_deep_link property :iphone_app_id @@ -34,7 +32,7 @@ class VideoAppDownloadCard property :googleplay_app_id property :googleplay_deep_link property :name - property :video_id + property :media_key RESOURCE_COLLECTION = "/#{TwitterAds::API_VERSION}/" \ 'accounts/%{account_id}/cards/video_app_download' # @api private diff --git a/lib/twitter-ads/creative/video_conversation_card.rb b/lib/twitter-ads/creative/video_conversation_card.rb index 1d46eb29..9daf7944 100644 --- a/lib/twitter-ads/creative/video_conversation_card.rb +++ b/lib/twitter-ads/creative/video_conversation_card.rb @@ -18,14 +18,14 @@ class VideoConversationCard property :deleted, type: :bool, read_only: true property :id, read_only: true property :updated_at, type: :time, read_only: true - property :video_url, read_only: true - property :video_poster_url, read_only: true + property :media_url, read_only: true + property :poster_media_url, read_only: true - property :cover_image_id - property :cover_video_id + property :unlocked_image_media_key + property :unlocked_video_media_key property :fourth_cta property :fourth_cta_tweet - property :image_media_id + property :poster_media_key property :first_cta property :first_cta_tweet property :name @@ -36,7 +36,7 @@ class VideoConversationCard property :third_cta property :third_cta_tweet property :title - property :video_id + property :media_key RESOURCE_COLLECTION = "/#{TwitterAds::API_VERSION}/" \ 'accounts/%{account_id}/cards/video_conversation' # @api private diff --git a/lib/twitter-ads/creative/video_website_card.rb b/lib/twitter-ads/creative/video_website_card.rb index cf2c25a6..aa46b345 100644 --- a/lib/twitter-ads/creative/video_website_card.rb +++ b/lib/twitter-ads/creative/video_website_card.rb @@ -19,21 +19,19 @@ class VideoWebsiteCard property :deleted, type: :bool, read_only: true property :id, read_only: true property :updated_at, type: :time, read_only: true - property :video_content_id, read_only: true property :video_height, read_only: true - property :video_hls_url, read_only: true property :video_owner_id, read_only: true property :video_poster_height, read_only: true - property :video_poster_url, read_only: true + property :poster_media_url, read_only: true property :video_poster_width, read_only: true - property :video_url, read_only: true + property :media_url, read_only: true property :video_width, read_only: true property :website_display_url, read_only: true property :website_dest_url, read_only: true property :name property :title - property :video_id + property :media_key property :website_url RESOURCE_COLLECTION = "/#{TwitterAds::API_VERSION}/accounts/%{account_id}/cards/video_website" diff --git a/lib/twitter-ads/creative/website_card.rb b/lib/twitter-ads/creative/website_card.rb index e696c426..17699cef 100644 --- a/lib/twitter-ads/creative/website_card.rb +++ b/lib/twitter-ads/creative/website_card.rb @@ -17,14 +17,14 @@ class WebsiteCard property :created_at, type: :time, read_only: true property :deleted, type: :bool, read_only: true property :id, read_only: true - property :image, read_only: true + property :media_url, read_only: true property :image_display_height, read_only: true property :image_display_width, read_only: true property :updated_at, type: :time, read_only: true property :website_dest_url, read_only: true property :website_display_url, read_only: true - property :image_media_id + property :media_key property :name property :website_title property :website_url diff --git a/lib/twitter-ads/enum.rb b/lib/twitter-ads/enum.rb index aa1a75ed..58f64d4b 100644 --- a/lib/twitter-ads/enum.rb +++ b/lib/twitter-ads/enum.rb @@ -106,6 +106,7 @@ module Entity FUNDING_INSTRUMENT = 'FUNDING_INSTRUMENT' CAMPAIGN = 'CAMPAIGN' LINE_ITEM = 'LINE_ITEM' + PROMOTED_ACCOUNT = 'PROMOTED_ACCOUNT' PROMOTED_TWEET = 'PROMOTED_TWEET' ORGANIC_TWEET = 'ORGANIC_TWEET' MEDIA_CREATIVE = 'MEDIA_CREATIVE' @@ -143,7 +144,10 @@ module MediaType end module Optimizations + APP_CLICKS = 'APP_CLICKS' + APP_INSTALLS = 'APP_INSTALLS' DEFAULT = 'DEFAULT' + ENGAGEMENTS = 'ENGAGEMENTS' WEBSITE_CONVERSIONS = 'WEBSITE_CONVERSIONS' end @@ -228,5 +232,11 @@ module TweetType PUBLISHED = 'PUBLISHED' SCHEDULED = 'SCHEDULED' end + + module TimelineType + ALL = 'ALL' + NULLCAST = 'NULLCAST' + ORGANIC = 'ORGANIC' + end end end diff --git a/lib/twitter-ads/resources/analytics.rb b/lib/twitter-ads/resources/analytics.rb index 1b14acae..57ddb911 100644 --- a/lib/twitter-ads/resources/analytics.rb +++ b/lib/twitter-ads/resources/analytics.rb @@ -5,15 +5,35 @@ require 'open-uri' module TwitterAds - module Analytics + class Analytics + include TwitterAds::DSL + include TwitterAds::Resource include TwitterAds::Enum + attr_reader :account + + property :id, read_only: true + property :id_str, read_only: true + property :status, read_only: true + property :url, read_only: true + property :created_at, type: :time, read_only: true + property :expires_at, type: :time, read_only: true + property :updated_at, type: :time, read_only: true + property :start_time, type: :time, read_only: true + property :end_time, type: :time, read_only: true + + property :entity, read_only: true + property :entity_ids, read_only: true + property :placement, read_only: true + property :granularity, read_only: true + property :metric_groups, read_only: true + ANALYTICS_MAP = { 'TwitterAds::Campaign' => Entity::CAMPAIGN, 'TwitterAds::LineItem' => Entity::LINE_ITEM, 'TwitterAds::OrganicTweet' => Entity::ORGANIC_TWEET, - 'TwitterAds::Creative::PromotedAccount' => Entity::ACCOUNT, + 'TwitterAds::Creative::PromotedAccount' => Entity::PROMOTED_ACCOUNT, 'TwitterAds::Creative::PromotedTweet' => Entity::PROMOTED_TWEET, 'TwitterAds::Creative::MediaCreative' => Entity::MEDIA_CREATIVE }.freeze @@ -25,42 +45,38 @@ module Analytics RESOURCE_ACTIVE_ENTITIES = "/#{TwitterAds::API_VERSION}/" + 'stats/accounts/%{account_id}/active_entities' # @api private - def self.included(klass) - klass.send :include, InstanceMethods - klass.extend ClassMethods + def initialize(account) + @account = account + self end - module InstanceMethods - - # Pulls a list of metrics for the current object instance. - # - # @example - # metric_groups = [:promoted_tweet_timeline_clicks, :promoted_tweet_search_clicks] - # object.stats(metrics) - # - # @param metric_groups [Array] A collection of metric groups to fetch. - # @param opts [Hash] An optional Hash of extended options. - # @option opts [Time] :start_time The starting time to use (default: 7 days ago). - # @option opts [Time] :end_time The end time to use (default: now). - # @option opts [Symbol] :granularity The granularity to use (default: :hour). - # - # @return [Array] The collection of stats requested. - # - # @see https://dev.twitter.com/ads/analytics/metrics-and-segmentation - # @since 1.0.0 - def stats(metric_groups, opts = {}) - self.class.stats(account, [id], metric_groups, opts) - end - + # Pulls a list of metrics for the current object instance. + # + # @example + # metric_groups = [MetricGroup::MOBILE_CONVERSION, MetricGroup::ENGAGEMENT] + # object.stats(metrics) + # + # @param metric_groups [Array] A collection of metric groups to fetch. + # @param opts [Hash] An optional Hash of extended options. + # @option opts [Time] :start_time The starting time to use (default: 7 days ago). + # @option opts [Time] :end_time The end time to use (default: now). + # @option opts [Symbol] :granularity The granularity to use (default: :hour). + # + # @return [Array] The collection of stats requested. + # + # @see https://dev.twitter.com/ads/analytics/metrics-and-segmentation + # @since 1.0.0 + def stats(metric_groups, opts = {}) + self.class.stats(account, [id], metric_groups, opts) end - module ClassMethods + class << self # Pulls a list of metrics for a specified set of object IDs. # # @example # ids = ['7o4em', 'oc9ce', '1c5lji'] - # metric_groups = [MetricGroups.MOBILE_CONVERSION, MetricGroups.ENGAGEMENT] + # metric_groups = [MetricGroup::MOBILE_CONVERSION, MetricGroup::ENGAGEMENT] # object.stats(account, ids, metric_groups) # # @param account [Account] The Account object instance. @@ -70,7 +86,7 @@ module ClassMethods # @option opts [Time] :start_time The starting time to use (default: 7 days ago). # @option opts [Time] :end_time The end time to use (default: now). # @option opts [Symbol] :granularity The granularity to use (default: :hour). - # @option opts [Symbol] :placement The placement of entity (default: ALL_ON_TWITTER). + # @option opts [String] :placement The placement of entity (default: ALL_ON_TWITTER). # # @return [Array] The collection of stats requested. # @@ -108,7 +124,7 @@ def stats(account, ids, metric_groups, opts = {}) # # @example # ids = ['7o4em', 'oc9ce', '1c5lji'] - # metric_groups = [MetricGroups.MOBILE_CONVERSION, MetricGroups.ENGAGEMENT] + # metric_groups = [MetricGroup::MOBILE_CONVERSION, MetricGroup::ENGAGEMENT] # object.create_async_job(account, ids, metric_groups) # # @param account [Account] The Account object instance. @@ -118,16 +134,17 @@ def stats(account, ids, metric_groups, opts = {}) # @option opts [Time] :start_time The starting time to use (default: 7 days ago). # @option opts [Time] :end_time The end time to use (default: now). # @option opts [Symbol] :granularity The granularity to use (default: :hour). - # @option opts [Symbol] :placement The placement of entity (default: ALL_ON_TWITTER). - # @option opts [Symbol] :segmentation_type The segmentation type to use (default: none). + # @option opts [String] :placement The placement of entity (default: ALL_ON_TWITTER). + # @option opts [String] :segmentation_type The segmentation type to use (default: none). # # @return The response of creating job # # @see https://dev.twitter.com/ads/analytics/metrics-and-segmentation - # @sync 1.0.0 + # @since 1.0.0 def create_async_job(account, ids, metric_groups, opts = {}) # set default metric values + entity = opts.fetch(:entity, name) end_time = opts.fetch(:end_time, (Time.now - Time.now.sec - (60 * Time.now.min))) start_time = opts.fetch(:start_time, end_time - 604_800) # 7 days ago granularity = opts.fetch(:granularity, :hour) @@ -143,7 +160,7 @@ def create_async_job(account, ids, metric_groups, opts = {}) start_time: TwitterAds::Utils.to_time(start_time, granularity, start_utc_offset), end_time: TwitterAds::Utils.to_time(end_time, granularity, end_utc_offset), granularity: granularity.to_s.upcase, - entity: ANALYTICS_MAP[name], + entity: ANALYTICS_MAP[entity], placement: placement, country: country, platform: platform @@ -153,19 +170,18 @@ def create_async_job(account, ids, metric_groups, opts = {}) params['entity_ids'] = ids.join(',') resource = self::RESOURCE_ASYNC_STATS % { account_id: account.id } - puts 'my resource is ' + resource response = Request.new(account.client, :post, resource, params: params).perform - response.body[:data] + TwitterAds::Analytics.new(account).from_response(response.body[:data], response.headers) end # Check async job status. # GET /#{TwitterAds::API_VERSION}/stats/jobs/accounts/:account_id # # @example - # TwitterAds::LineItem.check_async_job_status(account, job_id: '1357343438724431305') + # TwitterAds::LineItem.check_async_job_status(account, job_ids: ['1357343438724431305']) # # @param account [Account] The Account object instance. - # @option opts [String] :job_id The starting time to use (default: 7 days ago). + # @option opts [Array] :job_ids A collection of job IDs to fetch. # # @return A cursor of job statuses @@ -173,18 +189,18 @@ def check_async_job_status(account, opts = {}) # set default values job_ids = opts.fetch(:job_ids, nil) params = {} - params[:job_ids] = Array.wrap(job_ids).join(',') if job_ids + params[:job_ids] = job_ids.join(',') if job_ids resource = self::RESOURCE_ASYNC_STATS % { account_id: account.id } request = Request.new(account.client, :get, resource, params: params) - Cursor.new(nil, request, init_with: [account]) + Cursor.new(TwitterAds::Analytics, request, init_with: [account]) end # Fetch async job data for a completed job. # Raises HTTP 404 exception, otherwise retries up to 5 times with exponential backoff. # # @example - # response_data = TwitterAds::LineItem.fetch_async_job_data(account, file_url) + # response_data = TwitterAds::LineItem.fetch_async_job_data(account, data_url) # # @param data_url [String] The URL from the successful completion of an async job. # @@ -209,18 +225,46 @@ def fetch_async_job_data(data_url) end end + # Retrieve details about which entities' analytics metrics + # have changed in a given time period. + # + # @example + # time = Time.now + # utc_offset = '+09:00' + # start_time = time - (60 * 60 * 24) # -1 day + # end_time = time + # active_entities = TwitterAds::LineItem.active_entities( + # account, + # line_item_ids: %w(exrfs), + # start_time: start_time, + # end_time: end_time, + # utc_offset: utc_offset, + # granularity: :day) + # + # @param account [Account] The Account object instance. + # @param entity [String] The entity type to retrieve data for. + # @param start_time [Time] Scopes the retrieved data to the specified start time. + # @param end_time [Time] Scopes the retrieved data to the specified end time. + # @option opts [Array] :campaign_ids A collection of IDs to be fetched. + # @option opts [Array] :funding_instrument_ids A collection of IDs to be fetched. + # @option opts [Array] :line_item_ids A collection of IDs to be fetched. + # + # @return A list of entity details. + # + # @see https://developer.twitter.com/en/docs/ads/analytics/api-reference/active-entities + def active_entities(account, start_time:, end_time:, **opts) - entity_type = name + entity = opts.fetch(:entity, name) granularity = opts.fetch(:granularity, nil) start_utc_offset = opts[:start_utc_offset] || opts[:utc_offset] end_utc_offset = opts[:end_utc_offset] || opts[:utc_offset] - if entity_type == 'OrganicTweet' - raise "'OrganicTweet' not support with 'active_entities'" + if entity == 'OrganicTweet' + raise "'OrganicTweet' is not supported with 'active_entities'" end params = { - entity: ANALYTICS_MAP[entity_type], + entity: ANALYTICS_MAP[entity], start_time: TwitterAds::Utils.to_time(start_time, granularity, start_utc_offset), end_time: TwitterAds::Utils.to_time(end_time, granularity, end_utc_offset) } diff --git a/lib/twitter-ads/restapi.rb b/lib/twitter-ads/restapi.rb new file mode 100644 index 00000000..68c1f450 --- /dev/null +++ b/lib/twitter-ads/restapi.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true +# Copyright (C) 2019 Twitter, Inc. + +module TwitterRestApi + class UserIdLookup + include TwitterAds::DSL + include TwitterAds::Resource + + attr_reader :account + + property :id, read_only: true + property :id_str, read_only: true + property :screen_name, read_only: true + + DOMAIN = 'https://api.twitter.com' + RESOURCE = '/1.1/users/show.json' + + def self.load(account, opts = {}) + response = TwitterAds::Request.new( + account.client, + :get, + RESOURCE, + params: opts, + domain: DOMAIN + ).perform + new.from_response(response.body, response.headers) + end + end +end diff --git a/lib/twitter-ads/utils.rb b/lib/twitter-ads/utils.rb index af3006fa..3336b379 100644 --- a/lib/twitter-ads/utils.rb +++ b/lib/twitter-ads/utils.rb @@ -82,6 +82,17 @@ def extract_response_headers(headers) values end + def flatten_params(args) + params = args + params.each { |key, value| + if value.is_a?(Array) + next if value.empty? + params[key] = value.join(',') + end + } + params + end + end end diff --git a/lib/twitter-ads/version.rb b/lib/twitter-ads/version.rb index 80caff61..4fa87e21 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 = '5.2.0' + VERSION = '6.0.0' end diff --git a/spec/twitter-ads/campaign/targeting_criteria_spec.rb b/spec/twitter-ads/campaign/targeting_criteria_spec.rb index 924f30dc..3d7b33b6 100644 --- a/spec/twitter-ads/campaign/targeting_criteria_spec.rb +++ b/spec/twitter-ads/campaign/targeting_criteria_spec.rb @@ -31,7 +31,6 @@ targeting_type targeting_value tailored_audience_expansion - tailored_audience_type ) include_examples 'object property check', read, write diff --git a/spec/twitter-ads/creative/account_media_spec.rb b/spec/twitter-ads/creative/account_media_spec.rb deleted file mode 100644 index 92a269c9..00000000 --- a/spec/twitter-ads/creative/account_media_spec.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true -# Copyright (C) 2019 Twitter, Inc. - -require 'spec_helper' - -include TwitterAds::Enum - -describe TwitterAds::Creative::AccountMedia do - - before(:each) do - stub_fixture(:get, :accounts_all, "#{ADS_API}/accounts") - stub_fixture(:get, :accounts_load, "#{ADS_API}/accounts/2iqph") - end - - let(:client) do - Client.new( - Faker::Lorem.characters(15), - Faker::Lorem.characters(40), - "123456-#{Faker::Lorem.characters(40)}", - Faker::Lorem.characters(40) - ) - end - - let(:account) { client.accounts.first } - - # check model properties - subject { described_class.new(account) } - read = %w(id created_at updated_at deleted media_url) - write = %w(media_id creative_type video_id) - include_examples 'object property check', read, write - -end diff --git a/spec/twitter-ads/creative/image_app_download_card_spec.rb b/spec/twitter-ads/creative/image_app_download_card_spec.rb deleted file mode 100644 index f3704640..00000000 --- a/spec/twitter-ads/creative/image_app_download_card_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true -# Copyright (C) 2019 Twitter, Inc. - -require 'spec_helper' - -describe TwitterAds::Creative::ImageAppDownloadCard do - - before(:each) do - stub_fixture(:get, :accounts_all, "#{ADS_API}/accounts") - stub_fixture(:get, :accounts_load, "#{ADS_API}/accounts/2iqph") - end - - let(:client) do - Client.new( - Faker::Lorem.characters(15), - Faker::Lorem.characters(40), - "123456-#{Faker::Lorem.characters(40)}", - Faker::Lorem.characters(40) - ) - end - - let(:account) { client.accounts.first } - - # check model properties - subject { described_class.new(account) } - read = %w(id created_at updated_at deleted) - - write = %w( - name - country_code - iphone_app_id - iphone_deep_link - ipad_app_id - ipad_deep_link - googleplay_app_id - googleplay_deep_link - app_cta - wide_app_image_media_id - ) - - include_examples 'object property check', read, write - -end diff --git a/spec/twitter-ads/creative/image_conversation_card_spec.rb b/spec/twitter-ads/creative/image_conversation_card_spec.rb deleted file mode 100644 index 0781a76a..00000000 --- a/spec/twitter-ads/creative/image_conversation_card_spec.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true -# Copyright (C) 2019 Twitter, Inc. - -require 'spec_helper' - -describe TwitterAds::Creative::ImageConversationCard do - - before(:each) do - stub_fixture(:get, :accounts_all, "#{ADS_API}/accounts") - stub_fixture(:get, :accounts_load, "#{ADS_API}/accounts/2iqph") - end - - let(:client) do - Client.new( - Faker::Lorem.characters(15), - Faker::Lorem.characters(40), - "123456-#{Faker::Lorem.characters(40)}", - Faker::Lorem.characters(40) - ) - end - - let(:account) { client.accounts.first } - - # check model properties - subject { described_class.new(account) } - read = %w(id image deleted created_at updated_at) - write = %w( - name - title - first_cta - first_cta_tweet - second_cta - second_cta_tweet - thank_you_text - thank_you_url - image_media_id - ) - include_examples 'object property check', read, write - -end diff --git a/spec/twitter-ads/creative/video_app_download_card_spec.rb b/spec/twitter-ads/creative/video_app_download_card_spec.rb deleted file mode 100644 index 546f1624..00000000 --- a/spec/twitter-ads/creative/video_app_download_card_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -# frozen_string_literal: true -# Copyright (C) 2019 Twitter, Inc. - -require 'spec_helper' - -describe TwitterAds::Creative::VideoAppDownloadCard do - - before(:each) do - stub_fixture(:get, :accounts_all, "#{ADS_API}/accounts") - stub_fixture(:get, :accounts_load, "#{ADS_API}/accounts/2iqph") - end - - let(:client) do - Client.new( - Faker::Lorem.characters(15), - Faker::Lorem.characters(40), - "123456-#{Faker::Lorem.characters(40)}", - Faker::Lorem.characters(40) - ) - end - - let(:account) { client.accounts.first } - - # check model properties - subject { described_class.new(account) } - read = %w(id video_url video_poster_url deleted created_at updated_at) - write = %w( - name - country_code - iphone_app_id - iphone_deep_link - ipad_app_id - ipad_deep_link - googleplay_app_id - googleplay_deep_link - app_cta - image_media_id - video_id - ) - include_examples 'object property check', read, write - -end diff --git a/spec/twitter-ads/creative/video_conversation_card_spec.rb b/spec/twitter-ads/creative/video_conversation_card_spec.rb deleted file mode 100644 index 6609c8f1..00000000 --- a/spec/twitter-ads/creative/video_conversation_card_spec.rb +++ /dev/null @@ -1,51 +0,0 @@ -# frozen_string_literal: true -# Copyright (C) 2019 Twitter, Inc. - -require 'spec_helper' - -describe TwitterAds::Creative::VideoConversationCard do - - before(:each) do - stub_fixture(:get, :accounts_all, "#{ADS_API}/accounts") - stub_fixture(:get, :accounts_load, "#{ADS_API}/accounts/2iqph") - end - - let(:client) do - Client.new( - Faker::Lorem.characters(15), - Faker::Lorem.characters(40), - "123456-#{Faker::Lorem.characters(40)}", - Faker::Lorem.characters(40) - ) - end - - let(:account) { client.accounts.first } - - # check model properties - subject { described_class.new(account) } - - read = %w( - id - video_url - video_poster_url - deleted - created_at - updated_at - ) - - write = %w( - name - title - first_cta - first_cta_tweet - second_cta - second_cta_tweet - thank_you_text - thank_you_url - image_media_id - video_id - ) - - include_examples 'object property check', read, write - -end diff --git a/spec/twitter-ads/creative/website_card_spec.rb b/spec/twitter-ads/creative/website_card_spec.rb deleted file mode 100644 index fb1cd430..00000000 --- a/spec/twitter-ads/creative/website_card_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -# frozen_string_literal: true -# Copyright (C) 2019 Twitter, Inc. - -require 'spec_helper' - -describe TwitterAds::Creative::WebsiteCard do - - before(:each) do - stub_fixture(:get, :accounts_all, "#{ADS_API}/accounts") - stub_fixture(:get, :accounts_load, "#{ADS_API}/accounts/2iqph") - end - - let(:client) do - Client.new( - Faker::Lorem.characters(15), - Faker::Lorem.characters(40), - "123456-#{Faker::Lorem.characters(40)}", - Faker::Lorem.characters(40) - ) - end - - let(:account) { client.accounts.first } - - # check model properties - subject { described_class.new(account) } - read = %w( - card_type - card_uri - created_at - deleted - id - image - image_display_height - image_display_width - website_dest_url - website_display_url - updated_at - ) - write = %w(image_media_id name website_title website_url) - include_examples 'object property check', read, write - -end