bitlyr 0.9.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 (95) hide show
  1. data/History.txt +146 -0
  2. data/LICENSE +20 -0
  3. data/README.md +97 -0
  4. data/README.rdoc +37 -0
  5. data/Rakefile +43 -0
  6. data/VERSION +1 -0
  7. data/bitlyr.gemspec +154 -0
  8. data/lib/bitlyr/client.rb +153 -0
  9. data/lib/bitlyr/country.rb +10 -0
  10. data/lib/bitlyr/day.rb +12 -0
  11. data/lib/bitlyr/error.rb +13 -0
  12. data/lib/bitlyr/lib/core_ext/hash.rb +27 -0
  13. data/lib/bitlyr/lib/core_ext/string.rb +5 -0
  14. data/lib/bitlyr/missing_url.rb +12 -0
  15. data/lib/bitlyr/realtime_link.rb +16 -0
  16. data/lib/bitlyr/referrer.rb +12 -0
  17. data/lib/bitlyr/response.rb +39 -0
  18. data/lib/bitlyr/strategy/access_token.rb +26 -0
  19. data/lib/bitlyr/strategy/api_key.rb +33 -0
  20. data/lib/bitlyr/strategy/base.rb +26 -0
  21. data/lib/bitlyr/strategy/oauth.rb +50 -0
  22. data/lib/bitlyr/url.rb +124 -0
  23. data/lib/bitlyr/user.rb +100 -0
  24. data/lib/bitlyr.rb +39 -0
  25. data/test/fixtures/9uX1TE.json +1 -0
  26. data/test/fixtures/9uX1TEclicks.json +1 -0
  27. data/test/fixtures/9uX1TEclicks2.json +1 -0
  28. data/test/fixtures/9uX1TEinfo.json +1 -0
  29. data/test/fixtures/9uX1TEinfo2.json +1 -0
  30. data/test/fixtures/auth_fail.json +1 -0
  31. data/test/fixtures/auth_success.json +1 -0
  32. data/test/fixtures/betaworks.json +1 -0
  33. data/test/fixtures/betaworks2.json +1 -0
  34. data/test/fixtures/betaworks_jmp.json +1 -0
  35. data/test/fixtures/betaworks_other_user.json +1 -0
  36. data/test/fixtures/bitly9uX1TE.json +1 -0
  37. data/test/fixtures/bitly_pro_domain.json +1 -0
  38. data/test/fixtures/clicks_by_day.json +1 -0
  39. data/test/fixtures/clicks_by_day1.json +1 -0
  40. data/test/fixtures/clicks_by_day2.json +1 -0
  41. data/test/fixtures/clicks_by_minute1_url.json +1 -0
  42. data/test/fixtures/clicks_by_minute2_url.json +1 -0
  43. data/test/fixtures/clicks_by_minute_hash.json +1 -0
  44. data/test/fixtures/clicks_by_minute_hashes.json +1 -0
  45. data/test/fixtures/country_hash.json +1 -0
  46. data/test/fixtures/country_hash2.json +1 -0
  47. data/test/fixtures/country_url.json +1 -0
  48. data/test/fixtures/failure.json +1 -0
  49. data/test/fixtures/invalid_bitly_pro_domain.json +1 -0
  50. data/test/fixtures/invalid_credentials.json +1 -0
  51. data/test/fixtures/invalid_domain.json +1 -0
  52. data/test/fixtures/invalid_user.json +1 -0
  53. data/test/fixtures/invalid_x_api_key.json +1 -0
  54. data/test/fixtures/lookup_multiple_url.json +1 -0
  55. data/test/fixtures/lookup_not_real_url.json +1 -0
  56. data/test/fixtures/lookup_single_url.json +1 -0
  57. data/test/fixtures/missing_hash.json +1 -0
  58. data/test/fixtures/multiple_info.json +1 -0
  59. data/test/fixtures/multiple_url_click.json +1 -0
  60. data/test/fixtures/multiple_urls.json +1 -0
  61. data/test/fixtures/not_bitly_pro_domain.json +1 -0
  62. data/test/fixtures/not_found_info.json +1 -0
  63. data/test/fixtures/referrer_hash.json +1 -0
  64. data/test/fixtures/referrer_hash2.json +1 -0
  65. data/test/fixtures/referrer_url.json +1 -0
  66. data/test/fixtures/success.json +1 -0
  67. data/test/fixtures/url_info.json +1 -0
  68. data/test/fixtures/user_clicks.json +32 -0
  69. data/test/fixtures/user_countries.json +60 -0
  70. data/test/fixtures/user_realtime_links.json +15 -0
  71. data/test/fixtures/user_referrers.json +1 -0
  72. data/test/fixtures/valid_user.json +1 -0
  73. data/test/integration/strategy/test_api_key.rb +20 -0
  74. data/test/integration/strategy/test_oauth.rb +52 -0
  75. data/test/integration/test_client.rb +1415 -0
  76. data/test/integration/test_user.rb +97 -0
  77. data/test/test_helper.rb +54 -0
  78. data/test/unit/core_ext/test_hash.rb +69 -0
  79. data/test/unit/core_ext/test_string.rb +14 -0
  80. data/test/unit/strategy/test_access_token.rb +14 -0
  81. data/test/unit/strategy/test_api_key.rb +11 -0
  82. data/test/unit/strategy/test_base.rb +71 -0
  83. data/test/unit/strategy/test_oauth.rb +32 -0
  84. data/test/unit/test_bitly.rb +43 -0
  85. data/test/unit/test_client.rb +21 -0
  86. data/test/unit/test_country.rb +20 -0
  87. data/test/unit/test_day.rb +22 -0
  88. data/test/unit/test_error.rb +11 -0
  89. data/test/unit/test_missing.rb +34 -0
  90. data/test/unit/test_realtime_link.rb +30 -0
  91. data/test/unit/test_referrer.rb +20 -0
  92. data/test/unit/test_response.rb +86 -0
  93. data/test/unit/test_url.rb +155 -0
  94. data/test/unit/test_user.rb +17 -0
  95. metadata +233 -0
@@ -0,0 +1,13 @@
1
+ class BitlyrError < StandardError
2
+ attr_reader :code, :response
3
+
4
+ alias :msg :message
5
+
6
+ def initialize(response)
7
+ @response = response
8
+ @message = response.reason
9
+ @code = response.status
10
+
11
+ super("#{@message} - '#{@code}'")
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ class Hash
2
+ def to_query
3
+ map {|k, v|"#{k}=#{v}"}.sort * '&'
4
+ end
5
+
6
+ def stringify_keys!
7
+ keys.each do |key|
8
+ self[key.to_s] = delete(key)
9
+ end
10
+ self
11
+ end
12
+
13
+ def stringify_keys
14
+ dup.stringify_keys!
15
+ end
16
+
17
+ def symbolize_keys!
18
+ keys.each do |key|
19
+ self[(key.to_sym rescue key) || key] = delete(key)
20
+ end
21
+ self
22
+ end
23
+
24
+ def symbolize_keys
25
+ dup.symbolize_keys!
26
+ end
27
+ end
@@ -0,0 +1,5 @@
1
+ class String
2
+ def to_a
3
+ [ self ]
4
+ end
5
+ end
@@ -0,0 +1,12 @@
1
+ module Bitlyr
2
+ class MissingUrl
3
+ attr_accessor :short_url, :user_hash, :long_url, :error
4
+
5
+ def initialize(options={})
6
+ @error = options['error']
7
+ @long_url = options['long_url']
8
+ @short_url = options['short_url']
9
+ @user_hash = options['hash']
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module Bitlyr
2
+ # Day objects are created by the realtime_links method of a user
3
+ class RealtimeLink
4
+ attr_reader :clicks, :user_hash
5
+
6
+ def initialize(options = {})
7
+ @clicks = options['clicks']
8
+ @user_hash = options['user_hash']
9
+ end
10
+
11
+ # A convenience method to create a Bitlyr::Url from the data
12
+ def create_url(client)
13
+ Bitlyr::Url.new(client, 'user_clicks' => clicks, 'user_hash' => user_hash)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,12 @@
1
+ module Bitlyr
2
+ class Referrer
3
+ attr_reader :clicks, :referrer, :referrer_app, :url
4
+
5
+ def initialize(options)
6
+ @url = options['url']
7
+ @clicks = options['clicks']
8
+ @referrer = options['referrer']
9
+ @referrer_app = options['referrer_app']
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,39 @@
1
+ module Bitlyr
2
+ class Response
3
+ REASONS = { "OK" => "OK",
4
+ "INVALID_URI" => "Invalid URI" }.freeze
5
+
6
+ def initialize(response)
7
+ @response = response
8
+ end
9
+
10
+ def success?
11
+ parsed['status_code'] == 200
12
+ end
13
+
14
+ def status
15
+ parsed['status_code']
16
+ end
17
+
18
+ def reason
19
+ status = parsed['status_txt']
20
+ REASONS[ status ] || status.split('_').map(&:capitalize) * ' '
21
+ end
22
+
23
+ def body
24
+ parsed['data']
25
+ end
26
+
27
+ private
28
+ def parsed
29
+ case @response
30
+ when OAuth2::Response
31
+ @response.parsed
32
+ when HTTParty::Response
33
+ @response.parsed_response
34
+ else
35
+ raise "Unsupported Response type: #{@response.class}"
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,26 @@
1
+ module Bitlyr
2
+ module Strategy
3
+ class AccessToken < Base
4
+ extend Forwardable
5
+ delegate [ :client, :[] ] => :access_token
6
+
7
+ def initialize(access_token)
8
+ @access_token = access_token
9
+ end
10
+
11
+ private
12
+ # Make a request from the access token
13
+ def run_request(verb, method, options={})
14
+ access_token.send(verb, method, :params => authorization_params.merge(options), :parse => :json)
15
+ end
16
+
17
+ def access_token
18
+ @access_token
19
+ end
20
+
21
+ def authorization_params
22
+ { :access_token => access_token.token }
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,33 @@
1
+ module Bitlyr
2
+ module Strategy
3
+ class ApiKey < Base
4
+ include HTTParty
5
+ base_uri 'http://api.bit.ly/v3/'
6
+ query_string_normalizer proc { |query|
7
+ query.map do |key, value|
8
+ case value
9
+ when Array
10
+ value.map { |v| "#{key}=#{CGI.escape( v.to_s )}" }
11
+ else
12
+ "#{key}=#{CGI.escape( value.to_s )}"
13
+ end
14
+ end.compact.join('&')
15
+ }
16
+
17
+ def initialize(login, api_key)
18
+ @login = login
19
+ @api_key = api_key
20
+ end
21
+
22
+ private
23
+ # Make a request using the login and apiKey
24
+ def run_request(verb, method, options={})
25
+ self.class.send(verb, "/#{method}", :query => authorization_params.merge(options))
26
+ end
27
+
28
+ def authorization_params
29
+ { :login => @login, :apiKey => @api_key }
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,26 @@
1
+ module Bitlyr
2
+ module Strategy
3
+ class Base
4
+ def request(*args)
5
+ response = Bitlyr::Response.new(run_request(*args))
6
+ if response.success?
7
+ response.body
8
+ else
9
+ raise BitlyrError.new(response)
10
+ end
11
+ end
12
+
13
+ # Validates a login and api key
14
+ def validate(x_login, x_api_key)
15
+ response = request(:get, :validate, :x_login => x_login, :x_apiKey => x_api_key)
16
+ return response['valid'] == 1
17
+ end
18
+ alias :valid? :validate
19
+
20
+ private
21
+ def run_request(*args)
22
+ raise "Define this in the subclass"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,50 @@
1
+ module Bitlyr
2
+ module Strategy
3
+ class OAuth < Base
4
+ extend Forwardable
5
+ delegate [ :run_request ] => :access_token
6
+
7
+ BASE_URL = 'https://api-ssl.bit.ly/v3/'
8
+
9
+ def initialize(consumer_token, consumer_secret)
10
+ @consumer_token = consumer_token
11
+ @consumer_secret = consumer_secret
12
+ end
13
+
14
+ # Get the url to redirect a user to, pass the url you want the user
15
+ # to be redirected back to.
16
+ def authorize_url(redirect_url)
17
+ client.auth_code.authorize_url(:redirect_uri => redirect_url)
18
+ end
19
+
20
+ # Get the access token. You must pass the exact same redirect_url passed
21
+ # to the authorize_url method
22
+ def get_access_token_from_code(code, redirect_url)
23
+ access_token = client.auth_code.get_token(code, :redirect_uri => redirect_url, :parse => :query)
24
+ Bitlyr::Strategy::AccessToken.new(access_token)
25
+ end
26
+
27
+ # If you already have a user token, this method gets the access token
28
+ def get_access_token_from_token(token, params={})
29
+ params.stringify_keys!
30
+ access_token = ::OAuth2::AccessToken.new(client, token, params)
31
+ Bitlyr::Strategy::AccessToken.new(access_token)
32
+ end
33
+
34
+ # If you already have a user token, this method sets the access token
35
+ def set_access_token_from_token!(token, params={})
36
+ @access_token ||= get_access_token_from_token(token, params)
37
+ end
38
+
39
+ private
40
+ # Get the OAuth 2 client
41
+ def client
42
+ @client ||= ::OAuth2::Client.new(@consumer_token, @consumer_secret, :site => BASE_URL, :token_url => '/oauth/access_token')
43
+ end
44
+
45
+ def access_token
46
+ @access_token
47
+ end
48
+ end
49
+ end
50
+ end
data/lib/bitlyr/url.rb ADDED
@@ -0,0 +1,124 @@
1
+ module Bitlyr
2
+
3
+ # Url objects should only be created by the client object as it collects the correct information
4
+ # from the API.
5
+ class Url
6
+ attr_reader :short_url, :long_url, :user_hash, :global_hash, :referrers, :countries
7
+
8
+ # Initialize with a bitly client and optional hash to fill in the details for the url.
9
+ def initialize(client, options = {})
10
+ @client = client
11
+ @title = options['title'] || '' if options.key?('title')
12
+ @new_hash = (options['new_hash'] == 1)
13
+ @long_url = options['long_url']
14
+ @user_hash = options['hash'] || options['user_hash']
15
+ @short_url = options['url'] || options['short_url'] || "http://bit.ly/#{@user_hash}"
16
+ @created_by = options['created_by']
17
+ @global_hash = options['global_hash']
18
+ @user_clicks = options['user_clicks']
19
+ @global_clicks = options['global_clicks']
20
+
21
+ @referrers = options['referrers'].map{|referrer| Bitlyr::Referrer.new(referrer) } if options['referrers']
22
+ @countries = options['countries'].map{|country| Bitlyr::Country.new(country) } if options['countries']
23
+
24
+ if options['clicks'] && options['clicks'][0].is_a?(Hash)
25
+ @clicks_by_day = options['clicks'].map{|day| Bitlyr::Day.new(day)}
26
+ else
27
+ @clicks_by_minute = options['clicks']
28
+ end
29
+ end
30
+
31
+ # Returns true if the user hash was created first for this call
32
+ def new_hash?
33
+ @new_hash
34
+ end
35
+
36
+ # If the url already has click statistics, returns the user clicks.
37
+ # IF there are no click statistics or <tt>:force => true</tt> is passed,
38
+ # updates the stats and returns the user clicks
39
+ def user_clicks(options={})
40
+ update_clicks_data if @user_clicks.nil? || options[:force]
41
+ @user_clicks
42
+ end
43
+
44
+ # If the url already has click statistics, returns the global clicks.
45
+ # IF there are no click statistics or <tt>:force => true</tt> is passed,
46
+ # updates the stats and returns the global clicks
47
+ def global_clicks(options={})
48
+ update_clicks_data if @global_clicks.nil? || options[:force]
49
+ @global_clicks
50
+ end
51
+
52
+ # If the url already has the title, return it.
53
+ # IF there is no title or <tt>:force => true</tt> is passed,
54
+ # updates the info and returns the title
55
+ def title(options={})
56
+ update_info if @title.nil? || options[:force]
57
+ @title
58
+ end
59
+
60
+ # If the url already has the creator, return it.
61
+ # IF there is no creator or <tt>:force => true</tt> is passed,
62
+ # updates the info and returns the creator
63
+ def created_by(options={})
64
+ update_info if @created_by.nil? || options[:force]
65
+ @created_by
66
+ end
67
+
68
+ # If the url already has referrer data, return it.
69
+ # IF there is no referrer or <tt>:force => true</tt> is passed,
70
+ # updates the referrers and returns them
71
+ def referrers(options={})
72
+ update_referrers if @referrers.nil? || options[:force]
73
+ @referrers
74
+ end
75
+
76
+ # If the url already has country data, return it.
77
+ # IF there is no country or <tt>:force => true</tt> is passed,
78
+ # updates the countries and returns them
79
+ def countries(options={})
80
+ update_countries if @countries.nil? || options[:force]
81
+ @countries
82
+ end
83
+
84
+ def clicks_by_minute(options={})
85
+ if @clicks_by_minute.nil? || options[:force]
86
+ full_url = @client.clicks_by_minute(@user_hash || @short_url)
87
+ @clicks_by_minute = full_url.clicks_by_minute
88
+ end
89
+ @clicks_by_minute
90
+ end
91
+
92
+ def clicks_by_day(options={})
93
+ if @clicks_by_day.nil? || options[:force]
94
+ full_url = @client.clicks_by_day(@user_hash || @short_url)
95
+ @clicks_by_day = full_url.clicks_by_day
96
+ end
97
+ @clicks_by_day
98
+ end
99
+
100
+ private
101
+
102
+ def update_clicks_data
103
+ full_url = @client.clicks(@user_hash || @short_url)
104
+ @global_clicks = full_url.global_clicks
105
+ @user_clicks = full_url.user_clicks
106
+ end
107
+
108
+ def update_info
109
+ full_url = @client.info(@user_hash || @short_url)
110
+ @created_by = full_url.created_by
111
+ @title = full_url.title
112
+ end
113
+
114
+ def update_referrers
115
+ full_url = @client.referrers(@user_hash || @short_url)
116
+ @referrers = full_url.referrers
117
+ end
118
+
119
+ def update_countries
120
+ full_url = @client.countries(@user_hash || @short_url)
121
+ @countries = full_url.countries
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,100 @@
1
+ module Bitlyr
2
+
3
+ # A user requires an oauth access token. The flow is as follows:
4
+ #
5
+ # o = Bitlyr::Strategy::OAuth.new(consumer_token, consumer_secret)
6
+ # o.authorize_url(redirect_url)
7
+ # #=> "https://bit.ly/oauth/authorize?client_id=#{consumer_token}&type=web_server&redirect_uri=http%3A%2F%2Ftest.local%2Fbitlyr%2Fauth"
8
+ # Redirect your users to this url, when they authorize your application
9
+ # they will be redirected to the url you provided with a code parameter.
10
+ # Use that parameter, and the exact same redirect url as follows:
11
+ #
12
+ # o.get_access_token_from_code(params[:code], redirect_url)
13
+ # #=> #<Bitlyr::AccessToken ...>
14
+ #
15
+ # Then use that access token to create your user object.
16
+ #
17
+ # u=Bitlyr::User.new(o.access_token)
18
+ class User
19
+
20
+ def initialize(access_token)
21
+ @access_token = access_token
22
+ end
23
+
24
+ # OAuth 2 endpoint that provides a list of top referrers (up to 500 per
25
+ # day) for a given user’s bit.ly links, and the number of clicks per referrer.
26
+ #
27
+ # http://code.google.com/p/bitly-api/wiki/ApiDocumentation#/v3/user/referrers
28
+ def referrers(options={})
29
+ if @referrers.nil? || options.delete(:force)
30
+ @referrers = get_method(:referrers, Bitlyr::Referrer, options)
31
+ end
32
+ @referrers
33
+ end
34
+
35
+ # OAuth 2 endpoint that provides a list of countries from which clicks
36
+ # on a given user’s bit.ly links are originating, and the number of clicks per country.
37
+ #
38
+ # http://code.google.com/p/bitly-api/wiki/ApiDocumentation#/v3/user/countries
39
+ def countries(options={})
40
+ if @countries.nil? || options.delete(:force)
41
+ @countries = get_method(:countries, Bitlyr::Country, options)
42
+ end
43
+ @countries
44
+ end
45
+
46
+ # OAuth 2 endpoint that provides a given user’s 100 most popular links
47
+ # based on click traffic in the past hour, and the number of clicks per link.
48
+ #
49
+ # http://code.google.com/p/bitly-api/wiki/ApiDocumentation#/v3/user/realtime_links
50
+ def realtime_links(options={})
51
+ if @realtime_links.nil? || options.delete(:force)
52
+ result = get(:realtime_links, options)
53
+ @realtime_links = result['realtime_links'].map { |rs| Bitlyr::RealtimeLink.new(rs) }
54
+ end
55
+ @realtime_links
56
+ end
57
+
58
+ # OAuth 2 endpoint that provides the total clicks per day on a user’s bit.ly links.
59
+ #
60
+ # http://code.google.com/p/bitly-api/wiki/ApiDocumentation#/v3/user/clicks
61
+ def clicks(options={})
62
+ get_clicks(options)
63
+ @clicks
64
+ end
65
+
66
+ # Displays the total clicks returned from the clicks method.
67
+ def total_clicks(options={})
68
+ get_clicks(options)
69
+ @total_clicks
70
+ end
71
+
72
+ # Returns a Bitly Client using the credentials of the user.
73
+ def client
74
+ @client ||= Bitlyr::Client.new(@access_token)
75
+ end
76
+
77
+ private
78
+
79
+ def get_method(method, klass, options)
80
+ result = get(method, options)
81
+ result[method.to_s].map do |rs|
82
+ rs.map do |obj|
83
+ klass.new(obj)
84
+ end
85
+ end
86
+ end
87
+
88
+ def get_clicks(options={})
89
+ if @clicks.nil? || options.delete(:force)
90
+ result = get(:clicks, options)
91
+ @clicks = result['clicks'].map { |rs| Bitlyr::Day.new(rs) }
92
+ @total_clicks = result['total_clicks']
93
+ end
94
+ end
95
+
96
+ def get(method, options)
97
+ @access_token.request(:get, "user/#{method}", options)
98
+ end
99
+ end
100
+ end
data/lib/bitlyr.rb ADDED
@@ -0,0 +1,39 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'httparty'
4
+ require 'oauth2'
5
+ require 'cgi'
6
+ require 'forwardable'
7
+
8
+ module Bitlyr
9
+ def self.new(params = {})
10
+ params.symbolize_keys!
11
+ if params.key?(:client_id) && params.key?(:client_secret)
12
+ strategy = Bitlyr::Strategy::OAuth.new(params[:client_id], params[:client_secret])
13
+ strategy.get_access_token_from_token(params[:token]) if params[:token]
14
+ Bitlyr::Client.new strategy
15
+ elsif params.key?(:login) && params.key?(:api_key)
16
+ Bitlyr::Client.new Bitlyr::Strategy::ApiKey.new(params[:login], params[:api_key])
17
+ else
18
+ raise "requires a login and apiKey or client id and client secret"
19
+ end
20
+ end
21
+ end
22
+
23
+ require 'bitlyr/client'
24
+ require 'bitlyr/country'
25
+ require 'bitlyr/day'
26
+ require 'bitlyr/error'
27
+ require 'bitlyr/missing_url'
28
+ require 'bitlyr/realtime_link'
29
+ require 'bitlyr/referrer'
30
+ require 'bitlyr/response'
31
+ require 'bitlyr/url'
32
+ require 'bitlyr/user'
33
+ require 'bitlyr/strategy/base'
34
+ require 'bitlyr/strategy/access_token'
35
+ require 'bitlyr/strategy/api_key'
36
+ require 'bitlyr/strategy/oauth'
37
+
38
+ require 'bitlyr/lib/core_ext/hash'
39
+ require 'bitlyr/lib/core_ext/string'
@@ -0,0 +1 @@
1
+ { "status_code": 200, "status_txt": "OK", "data": { "expand": [ { "hash": "9uX1TE", "long_url": "http:\/\/betaworks.com\/", "user_hash": "9uX1TE", "global_hash": "18H1ET" } ] } }
@@ -0,0 +1 @@
1
+ {"status_code": 200, "data": {"clicks": [{"user_clicks": 0, "global_hash": "18H1ET", "hash": "9uX1TE", "user_hash": "9uX1TE", "global_clicks": 81}]}, "status_txt": "OK"}
@@ -0,0 +1 @@
1
+ {"status_code": 200, "data": {"clicks": [{"user_clicks": 1, "global_hash": "18H1ET", "hash": "9uX1TE", "user_hash": "9uX1TE", "global_clicks": 82}]}, "status_txt": "OK"}
@@ -0,0 +1 @@
1
+ {"status_code": 200, "data": {"info": [{"global_hash": "18H1ET", "hash": "9uX1TE", "user_hash": "9uX1TE", "created_by": "philnash", "title": "A title"}]}, "status_txt": "OK"}
@@ -0,0 +1 @@
1
+ {"status_code": 200, "data": {"info": [{"global_hash": "18H1ET", "hash": "9uX1TE", "user_hash": "9uX1TE", "created_by": "philnash2", "title": "A New Title"}]}, "status_txt": "OK"}
@@ -0,0 +1 @@
1
+ {"data":{"authenticate":{"successful": false}},"status_code": 200,"status_txt": "OK"}
@@ -0,0 +1 @@
1
+ {"data":{"authenticate":{"api_key": "R_0da49e0a9118ff35f52f629d2d71bf07","successful": true,"username": "bitlyapidemo"}},"status_code": 200,"status_txt": "OK"}
@@ -0,0 +1 @@
1
+ { "status_code": 200, "status_txt": "OK", "data": { "long_url": "http:\/\/betaworks.com\/", "url": "http:\/\/bit.ly\/9uX1TE", "hash": "9uX1TE", "global_hash": "18H1ET", "new_hash": 1 } }
@@ -0,0 +1 @@
1
+ { "status_code": 200, "status_txt": "OK", "data": { "long_url": "http:\/\/betaworks.com\/", "url": "http:\/\/bit.ly\/9uX1TE", "hash": "9uX1TE", "global_hash": "18H1ET", "new_hash": 0 } }
@@ -0,0 +1 @@
1
+ { "status_code": 200, "status_txt": "OK", "data": { "long_url": "http:\/\/betaworks.com\/", "url": "http:\/\/j.mp\/9uX1TE", "hash": "9uX1TE", "global_hash": "18H1ET", "new_hash": 0 } }
@@ -0,0 +1 @@
1
+ { "status_code": 200, "status_txt": "OK", "data": { "long_url": "http:\/\/betaworks.com\/", "url": "http:\/\/bit.ly\/cWJlxM", "hash": "cWJlxM", "global_hash": "18H1ET", "new_hash": 1 } }
@@ -0,0 +1 @@
1
+ { "status_code": 200, "status_txt": "OK", "data": { "expand": [ { "short_url": "http:\/\/bit.ly\/9uX1TE", "long_url": "http:\/\/betaworks.com\/", "user_hash": "9uX1TE", "global_hash": "18H1ET" } ] } }
@@ -0,0 +1 @@
1
+ {"status_code": 200, "data": {"domain": "nyti.ms", "bitly_pro_domain": true}, "status_txt": "OK"}
@@ -0,0 +1 @@
1
+ {"status_code": 200, "data": {"clicks_by_day": [{"global_hash": "9DguyN", "hash": "9DguyN", "user_hash": "9DguyN", "clicks": [{"clicks": 1, "day_start": 1290488400}, {"clicks": 0, "day_start": 1290402000}, {"clicks": 0, "day_start": 1290315600}, {"clicks": 1, "day_start": 1290229200}, {"clicks": 0, "day_start": 1290142800}, {"clicks": 0, "day_start": 1290056400}, {"clicks": 0, "day_start": 1289970000}]}, {"global_hash": "dpC5ns", "hash": "dvxi6W", "user_hash": "dvxi6W", "clicks": [{"clicks": 1, "day_start": 1290488400}, {"clicks": 0, "day_start": 1290402000}, {"clicks": 0, "day_start": 1290315600}, {"clicks": 0, "day_start": 1290229200}, {"clicks": 0, "day_start": 1290142800}, {"clicks": 0, "day_start": 1290056400}, {"clicks": 0, "day_start": 1289970000}]}]}, "status_txt": "OK"}
@@ -0,0 +1 @@
1
+ {"status_code": 200, "data": {"clicks_by_day": [{"global_hash": "9DguyN", "hash": "9DguyN", "user_hash": "9DguyN", "clicks": [{"clicks": 1, "day_start": 1290488400}, {"clicks": 0, "day_start": 1290402000}, {"clicks": 0, "day_start": 1290315600}, {"clicks": 1, "day_start": 1290229200}, {"clicks": 0, "day_start": 1290142800}, {"clicks": 0, "day_start": 1290056400}, {"clicks": 0, "day_start": 1289970000}]}]}, "status_txt": "OK"}
@@ -0,0 +1 @@
1
+ {"status_code": 200, "data": {"clicks_by_day": [{"global_hash": "9DguyN", "hash": "9DguyN", "user_hash": "9DguyN", "clicks": [{"clicks": 2, "day_start": 1290488400}, {"clicks": 0, "day_start": 1290402000}, {"clicks": 0, "day_start": 1290315600}, {"clicks": 1, "day_start": 1290229200}, {"clicks": 0, "day_start": 1290142800}, {"clicks": 0, "day_start": 1290056400}, {"clicks": 0, "day_start": 1289970000}]}]}, "status_txt": "OK"}
@@ -0,0 +1 @@
1
+ {"status_code": 200, "data": {"clicks_by_minute": [{"short_url": "http://j.mp/9DguyN", "global_hash": "9DguyN", "user_hash": "9DguyN", "clicks": [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}]}, "status_txt": "OK"}
@@ -0,0 +1 @@
1
+ {"status_code": 200, "data": {"clicks_by_minute": [{"short_url": "http://j.mp/9DguyN", "global_hash": "9DguyN", "user_hash": "9DguyN", "clicks": [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}]}, "status_txt": "OK"}
@@ -0,0 +1 @@
1
+ {"status_code": 200, "data": {"clicks_by_minute": [{"global_hash": "9DguyN", "hash": "9DguyN", "user_hash": "9DguyN", "clicks": [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}]}, "status_txt": "OK"}
@@ -0,0 +1 @@
1
+ {"status_code": 200, "data": {"clicks_by_minute": [{"global_hash": "9DguyN", "hash": "9DguyN", "user_hash": "9DguyN", "clicks": [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}, {"global_hash": "dpC5ns", "hash": "dvxi6W", "user_hash": "dvxi6W", "clicks": [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}]}, "status_txt": "OK"}
@@ -0,0 +1 @@
1
+ {"status_code": 200, "data": {"global_hash": "djZ9g4", "hash": "djZ9g4", "user_hash": "djZ9g4", "countries": [{"country": "US", "clicks": 58}, {"country": "GB", "clicks": 14}, {"country": null, "clicks": 14}, {"country": "BR", "clicks": 6}, {"country": "CA", "clicks": 6}, {"country": "FR", "clicks": 5}, {"country": "AU", "clicks": 4}, {"country": "IN", "clicks": 4}, {"country": "DE", "clicks": 3}, {"country": "TW", "clicks": 3}, {"country": "IT", "clicks": 2}, {"country": "NO", "clicks": 2}, {"country": "PL", "clicks": 2}, {"country": "BE", "clicks": 1}, {"country": "BG", "clicks": 1}, {"country": "CI", "clicks": 1}, {"country": "CZ", "clicks": 1}, {"country": "ES", "clicks": 1}, {"country": "GR", "clicks": 1}, {"country": "HK", "clicks": 1}, {"country": "KR", "clicks": 1}, {"country": "MY", "clicks": 1}, {"country": "PH", "clicks": 1}, {"country": "PT", "clicks": 1}, {"country": "SE", "clicks": 1}, {"country": "VE", "clicks": 1}]}, "status_txt": "OK"}
@@ -0,0 +1 @@
1
+ {"status_code": 200, "data": {"global_hash": "djZ9g4", "hash": "djZ9g4", "user_hash": "djZ9g4", "countries": [{"country": "US", "clicks": 59}, {"country": "GB", "clicks": 14}, {"country": null, "clicks": 14}, {"country": "BR", "clicks": 6}, {"country": "CA", "clicks": 6}, {"country": "FR", "clicks": 5}, {"country": "AU", "clicks": 4}, {"country": "IN", "clicks": 4}, {"country": "DE", "clicks": 3}, {"country": "TW", "clicks": 3}, {"country": "IT", "clicks": 2}, {"country": "NO", "clicks": 2}, {"country": "PL", "clicks": 2}, {"country": "BE", "clicks": 1}, {"country": "BG", "clicks": 1}, {"country": "CI", "clicks": 1}, {"country": "CZ", "clicks": 1}, {"country": "ES", "clicks": 1}, {"country": "GR", "clicks": 1}, {"country": "HK", "clicks": 1}, {"country": "KR", "clicks": 1}, {"country": "MY", "clicks": 1}, {"country": "PH", "clicks": 1}, {"country": "PT", "clicks": 1}, {"country": "SE", "clicks": 1}, {"country": "VE", "clicks": 1}]}, "status_txt": "OK"}
@@ -0,0 +1 @@
1
+ {"status_code": 200, "data": {"short_url": "http://bit.ly/djZ9g4", "global_hash": "djZ9g4", "user_hash": "djZ9g4", "countries": [{"country": "US", "clicks": 58}, {"country": "GB", "clicks": 14}, {"country": null, "clicks": 14}, {"country": "BR", "clicks": 6}, {"country": "CA", "clicks": 6}, {"country": "FR", "clicks": 5}, {"country": "AU", "clicks": 4}, {"country": "IN", "clicks": 4}, {"country": "DE", "clicks": 3}, {"country": "TW", "clicks": 3}, {"country": "IT", "clicks": 2}, {"country": "NO", "clicks": 2}, {"country": "PL", "clicks": 2}, {"country": "BE", "clicks": 1}, {"country": "BG", "clicks": 1}, {"country": "CI", "clicks": 1}, {"country": "CZ", "clicks": 1}, {"country": "ES", "clicks": 1}, {"country": "GR", "clicks": 1}, {"country": "HK", "clicks": 1}, {"country": "KR", "clicks": 1}, {"country": "MY", "clicks": 1}, {"country": "PH", "clicks": 1}, {"country": "PT", "clicks": 1}, {"country": "SE", "clicks": 1}, {"country": "VE", "clicks": 1}]}, "status_txt": "OK"}
@@ -0,0 +1 @@
1
+ { "status_code": 400, "status_txt": "Bad Request", "data": { } }
@@ -0,0 +1 @@
1
+ {"status_code": 500, "data": null, "status_txt": "INVALID_DOMAIN"}
@@ -0,0 +1 @@
1
+ { "data": [ ], "status_code": 500, "status_txt": "INVALID_LOGIN" }
@@ -0,0 +1 @@
1
+ { "data": [ ], "status_code": 500, "status_txt": "INVALID_ARG_DOMAIN" }
@@ -0,0 +1 @@
1
+ { "status_code": 200, "status_txt": "OK", "data": { "valid": 0 } }
@@ -0,0 +1 @@
1
+ { "data": [ ], "status_code": 500, "status_txt": "INVALID_X_APIKEY" }