shingara-garb 0.7.6

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 (41) hide show
  1. data/README.md +255 -0
  2. data/Rakefile +56 -0
  3. data/lib/garb.rb +42 -0
  4. data/lib/garb/account.rb +29 -0
  5. data/lib/garb/authentication_request.rb +53 -0
  6. data/lib/garb/data_request.rb +42 -0
  7. data/lib/garb/filter_parameters.rb +41 -0
  8. data/lib/garb/profile.rb +37 -0
  9. data/lib/garb/profile_reports.rb +15 -0
  10. data/lib/garb/report.rb +26 -0
  11. data/lib/garb/report_parameter.rb +25 -0
  12. data/lib/garb/report_response.rb +33 -0
  13. data/lib/garb/reports.rb +5 -0
  14. data/lib/garb/reports/bounces.rb +5 -0
  15. data/lib/garb/reports/exits.rb +5 -0
  16. data/lib/garb/reports/pageviews.rb +5 -0
  17. data/lib/garb/reports/unique_pageviews.rb +5 -0
  18. data/lib/garb/reports/visits.rb +5 -0
  19. data/lib/garb/resource.rb +92 -0
  20. data/lib/garb/session.rb +35 -0
  21. data/lib/garb/version.rb +13 -0
  22. data/lib/support.rb +39 -0
  23. data/test/fixtures/cacert.pem +67 -0
  24. data/test/fixtures/profile_feed.xml +38 -0
  25. data/test/fixtures/report_feed.xml +46 -0
  26. data/test/test_helper.rb +18 -0
  27. data/test/unit/garb/account_test.rb +53 -0
  28. data/test/unit/garb/authentication_request_test.rb +121 -0
  29. data/test/unit/garb/data_request_test.rb +119 -0
  30. data/test/unit/garb/filter_parameters_test.rb +59 -0
  31. data/test/unit/garb/oauth_session_test.rb +11 -0
  32. data/test/unit/garb/profile_reports_test.rb +29 -0
  33. data/test/unit/garb/profile_test.rb +75 -0
  34. data/test/unit/garb/report_parameter_test.rb +43 -0
  35. data/test/unit/garb/report_response_test.rb +14 -0
  36. data/test/unit/garb/report_test.rb +91 -0
  37. data/test/unit/garb/resource_test.rb +38 -0
  38. data/test/unit/garb/session_test.rb +91 -0
  39. data/test/unit/garb_test.rb +14 -0
  40. data/test/unit/symbol_operator_test.rb +37 -0
  41. metadata +146 -0
@@ -0,0 +1,38 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <feed xmlns="http://www.w3.org/2005/Atom" xmlns:dxp="http://schemas.google.com/analytics/2009" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">
3
+ <id>http://www.google.com/analytics/feeds/accounts/tpitale@gmail.com</id>
4
+ <updated>2009-10-02T07:47:35.000-07:00</updated>
5
+ <title type="text">Profile list for tpitale@gmail.com</title>
6
+ <link rel="self" type="application/atom+xml" href="http://www.google.com/analytics/feeds/accounts/default"/>
7
+ <author><name>Google Analytics</name></author>
8
+ <generator version="1.0">Google Analytics</generator>
9
+ <openSearch:totalResults>2</openSearch:totalResults>
10
+ <openSearch:startIndex>1</openSearch:startIndex>
11
+ <openSearch:itemsPerPage>2</openSearch:itemsPerPage>
12
+ <entry>
13
+ <id>http://www.google.com/analytics/feeds/accounts/ga:12345</id>
14
+ <updated>2008-07-21T14:05:57.000-07:00</updated>
15
+ <title type="text">Historical</title>
16
+ <link rel="alternate" type="text/html" href="http://www.google.com/analytics"/>
17
+ <dxp:property name="ga:accountId" value="1111"/>
18
+ <dxp:property name="ga:accountName" value="Blog Beta"/>
19
+ <dxp:property name="ga:profileId" value="1212"/>
20
+ <dxp:property name="ga:webPropertyId" value="UA-1111-1"/>
21
+ <dxp:property name="ga:currency" value="USD"/>
22
+ <dxp:property name="ga:timezone" value="America/New_York"/>
23
+ <dxp:tableId>ga:12345</dxp:tableId>
24
+ </entry>
25
+ <entry>
26
+ <id>http://www.google.com/analytics/feeds/accounts/ga:12346</id>
27
+ <updated>2008-11-24T11:51:07.000-08:00</updated>
28
+ <title type="text">Presently</title>
29
+ <link rel="alternate" type="text/html" href="http://www.google.com/analytics"/>
30
+ <dxp:property name="ga:accountId" value="1111"/>
31
+ <dxp:property name="ga:accountName" value="Blog Beta"/>
32
+ <dxp:property name="ga:profileId" value="1213"/>
33
+ <dxp:property name="ga:webPropertyId" value="UA-1111-2"/>
34
+ <dxp:property name="ga:currency" value="USD"/>
35
+ <dxp:property name="ga:timezone" value="America/New_York"/>
36
+ <dxp:tableId>ga:12346</dxp:tableId>
37
+ </entry>
38
+ </feed>
@@ -0,0 +1,46 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <feed xmlns="http://www.w3.org/2005/Atom"
3
+ xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/"
4
+ xmlns:dxp="http://schemas.google.com/analytics/2009"
5
+ xmlns:ga="http://schemas.google.com/analytics/2008">
6
+ <id>http://www.google.com/analytics/feeds/data?ids=ga:983247&amp;dimensions=ga:country,ga:city&amp;metrics=ga:pageViews&amp;start-date=2008-01-01&amp;end-date=2008-01-02</id>
7
+ <updated>2008-01-02T15:59:59.999-08:00 </updated>
8
+ <title type="text">Google Analytics Data for Profile 983247</title>
9
+ <link href="http://www.google.com/analytics/feeds/data" rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml"/>
10
+ <link href="http://www.google.com/analytics/feeds/data?end-date=2008-01-02&amp;start-date=2008-01-01&amp;metrics=ga%3ApageViews&amp;ids=ga%3A983247&amp;dimensions=ga%3Acountry%2Cga%3Acity" rel="self" type="application/atom+xml"/>
11
+ <link href="http://www.google.com/analytics/feeds/data?start-index=1001&amp;max-results=1000&amp;end-date=2008-01-02&amp;start-date=2008-01-01&amp;metrics=ga%3ApageViews&amp;ids=ga%3A983247&amp;dimensions=ga%3Acountry%2Cga%3Acity" rel="next" type="application/atom+xml"/>
12
+ <author>
13
+ <name>Google Analytics</name>
14
+ </author>
15
+ <openSearch:startIndex>3</openSearch:startIndex>
16
+ <openSearch:itemsPerPage>4</openSearch:itemsPerPage>
17
+ <ga:webPropertyID>UA-983247-67</ga:webPropertyID>
18
+ <ga:start-date>2008-01-01</ga:start-date>
19
+ <ga:end-date>2008-01-02</ga:end-date>
20
+
21
+ <entry>
22
+ <id> http://www.google.com/analytics/feeds/data?ids=ga:1174&amp;ga:country=%28not%20set%29&amp;ga:city=%28not%20set%29&amp;start-date=2008-01-01&amp;end-date=2008-01-02 </id>
23
+ <updated> 2008-01-01T16:00:00.001-08:00 </updated>
24
+ <title type="text"> ga:country=(not set) | ga:city=(not set) </title>
25
+ <link href="http://www.google.com/analytics/feeds/data" rel="self" type="application/atom+xml"/>
26
+ <dxp:dimension name="ga:country" value="(not set)" />
27
+ <dxp:dimension name="ga:city" value="(not set)" />
28
+ <dxp:metric name="ga:pageviews" value="33" />
29
+ </entry>
30
+ <entry>
31
+ <id> http://www.google.com/analytics/feeds/data?ids=ga:1174&amp;ga:country=Afghanistan&amp;ga:city=Kabul&amp;start-date=2008-01-01&amp;end-date=2008-01-02 </id>
32
+ <updated> 2008-01-01T16:00:00.001-08:00 </updated>
33
+ <title type="text"> ga:country=Afghanistan | ga:city=Kabul </title>
34
+ <dxp:dimension name="ga:country" value="Afghanistan" />
35
+ <dxp:dimension name="ga:city" value="Kabul" />
36
+ <dxp:metric name="ga:pageviews" value="2" />
37
+ </entry>
38
+ <entry>
39
+ <id> http://www.google.com/analytics/feeds/data?ids=ga:1174&amp;ga:country=Albania&amp;ga:city=Tirana&amp;start-date=2008-01-01&amp;end-date=2008-01-02 </id>
40
+ <updated> 2008-01-01T16:00:00.001-08:00 </updated>
41
+ <title type="text"> ga:country=Albania | ga:city=Tirana </title>
42
+ <dxp:dimension name="ga:country" value="Albania" />
43
+ <dxp:dimension name="ga:city" value="Tirana" />
44
+ <dxp:metric name="ga:pageviews" value="1" />
45
+ </entry>
46
+ </feed>
@@ -0,0 +1,18 @@
1
+ $:.reject! { |e| e.include? 'TextMate' }
2
+
3
+ require 'rubygems'
4
+ require 'minitest/unit'
5
+ require 'shoulda'
6
+ require 'mocha'
7
+
8
+ require File.dirname(__FILE__) + '/../lib/garb'
9
+
10
+ class MiniTest::Unit::TestCase
11
+
12
+ def read_fixture(filename)
13
+ File.read(File.dirname(__FILE__) + "/fixtures/#{filename}")
14
+ end
15
+
16
+ end
17
+
18
+ MiniTest::Unit.autorun
@@ -0,0 +1,53 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', '/test_helper')
2
+
3
+ module Garb
4
+ class AccountTest < MiniTest::Unit::TestCase
5
+ context "The Account class" do
6
+ should "have an array of accounts with all profiles" do
7
+ p1 = stub(:account_id => '1111', :account_name => 'Blog 1')
8
+ p2 = stub(:account_id => '1112', :account_name => 'Blog 2')
9
+
10
+ Profile.stubs(:all).returns([p1,p2,p1,p2])
11
+ Account.stubs(:new).returns('account1', 'account2')
12
+
13
+ assert_equal ['account1','account2'], Account.all
14
+ assert_received(Profile, :all)
15
+ assert_received(Account, :new) {|e| e.with([p1,p1])}
16
+ assert_received(Account, :new) {|e| e.with([p2,p2])}
17
+ end
18
+ end
19
+
20
+ context "An instance of the Account class" do
21
+ setup do
22
+ profile = stub(:account_id => '1111', :account_name => 'Blog 1')
23
+ @profiles = [profile,profile]
24
+ @account = Account.new(@profiles)
25
+ end
26
+
27
+ context "all" do
28
+ should "use an optional user session" do
29
+ session = Session.new
30
+ Garb::Profile.expects(:all).with(session).returns(@profiles)
31
+
32
+ accounts = Account.all(session)
33
+ assert_equal 1, accounts.size
34
+ assert_equal @profiles, accounts.first.profiles
35
+ end
36
+ end
37
+
38
+ context "when creating a new account from an array of profiles" do
39
+ should "take the account id from the first profile" do
40
+ assert_equal @profiles.first.account_id, @account.id
41
+ end
42
+
43
+ should "take the account name from the first profile" do
44
+ assert_equal @profiles.first.account_name, @account.name
45
+ end
46
+
47
+ should "store the array of profiles" do
48
+ assert_equal @profiles, @account.profiles
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,121 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', '/test_helper')
2
+
3
+ CA_CERT_FILE = File.join(File.dirname(__FILE__), '..', '/cacert.pem')
4
+
5
+ module Garb
6
+ class AuthenticationRequestTest < MiniTest::Unit::TestCase
7
+
8
+ context "An instance of the AuthenticationRequest class" do
9
+
10
+ setup { @request = AuthenticationRequest.new('email', 'password') }
11
+
12
+ should "have a collection of parameters that include the email and password" do
13
+ expected =
14
+ {
15
+ 'Email' => 'user@example.com',
16
+ 'Passwd' => 'fuzzybunnies',
17
+ 'accountType' => 'HOSTED_OR_GOOGLE',
18
+ 'service' => 'analytics',
19
+ 'source' => 'vigetLabs-garb-001'
20
+ }
21
+
22
+ request = AuthenticationRequest.new('user@example.com', 'fuzzybunnies')
23
+ assert_equal expected, request.parameters
24
+ end
25
+
26
+ should "have a URI" do
27
+ assert_equal URI.parse('https://www.google.com/accounts/ClientLogin'), @request.uri
28
+ end
29
+
30
+ should "be able to send a request to the GAAPI service with proper ssl" do
31
+ @request.expects(:build_request).returns('post')
32
+
33
+ response = mock {|m| m.expects(:is_a?).with(Net::HTTPOK).returns(true) }
34
+
35
+ http = mock do |m|
36
+ m.expects(:use_ssl=).with(true)
37
+ m.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
38
+ m.expects(:ca_file=).with(CA_CERT_FILE)
39
+ m.expects(:request).with('post').yields(response)
40
+ end
41
+
42
+ Net::HTTP.expects(:new).with('www.google.com', 443).returns(http)
43
+
44
+ @request.send_request(OpenSSL::SSL::VERIFY_PEER)
45
+ end
46
+
47
+ should "be able to send a request to the GAAPI service with ignoring ssl" do
48
+ @request.expects(:build_request).returns('post')
49
+
50
+ response = mock {|m| m.expects(:is_a?).with(Net::HTTPOK).returns(true) }
51
+
52
+ http = mock do |m|
53
+ m.expects(:use_ssl=).with(true)
54
+ m.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
55
+ m.expects(:request).with('post').yields(response)
56
+ end
57
+
58
+ Net::HTTP.expects(:new).with('www.google.com', 443).returns(http)
59
+
60
+ @request.send_request(OpenSSL::SSL::VERIFY_NONE)
61
+ end
62
+
63
+ should "be able to build a request for the GAAPI service" do
64
+ params = "param"
65
+ @request.expects(:parameters).with().returns(params)
66
+
67
+ post = mock
68
+ post.expects(:set_form_data).with(params)
69
+
70
+ Net::HTTP::Post.expects(:new).with('/accounts/ClientLogin').returns(post)
71
+
72
+ @request.build_request
73
+ end
74
+
75
+ should "be able to retrieve an auth_token from the body" do
76
+ response_data =
77
+ "SID=mysid\n" +
78
+ "LSID=mylsid\n" +
79
+ "Auth=auth_token\n"
80
+
81
+ @request.expects(:send_request).with(OpenSSL::SSL::VERIFY_NONE).returns(stub(:body => response_data))
82
+
83
+ assert_equal 'auth_token', @request.auth_token
84
+ end
85
+
86
+ should "use VERIFY_PEER if auth_token needs to be secure" do
87
+ response_data =
88
+ "SID=mysid\n" +
89
+ "LSID=mylsid\n" +
90
+ "Auth=auth_token\n"
91
+
92
+ @request.expects(:send_request).with(OpenSSL::SSL::VERIFY_PEER).returns(stub(:body => response_data))
93
+
94
+ assert_equal 'auth_token', @request.auth_token(:secure => true)
95
+ end
96
+
97
+ should "raise an exception when requesting an auth_token when the authorization fails" do
98
+ @request.stubs(:build_request)
99
+ response = mock do |m|
100
+ m.expects(:is_a?).with(Net::HTTPOK).returns(false)
101
+ end
102
+
103
+ http = stub do |s|
104
+ s.stubs(:use_ssl=)
105
+ s.stubs(:verify_mode=)
106
+ s.stubs(:request).yields(response)
107
+ end
108
+
109
+ Net::HTTP.stubs(:new).with('www.google.com', 443).returns(http)
110
+
111
+ assert_raises(Garb::AuthenticationRequest::AuthError) do
112
+ @request.send_request(OpenSSL::SSL::VERIFY_NONE)
113
+ end
114
+ end
115
+
116
+ end
117
+
118
+
119
+
120
+ end
121
+ end
@@ -0,0 +1,119 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', '/test_helper')
2
+
3
+ module Garb
4
+ class DataRequestTest < MiniTest::Unit::TestCase
5
+
6
+ context "An instance of the DataRequest class" do
7
+ setup do
8
+ @session = Session.new
9
+ @session.auth_token = 'abcdefg123456'
10
+ end
11
+
12
+ should "be able to build the query string from parameters" do
13
+ parameters = {'ids' => '12345', 'metrics' => 'country'}
14
+ data_request = DataRequest.new(@session, "", parameters)
15
+
16
+ query_string = data_request.query_string
17
+
18
+ assert_match(/^\?/, query_string)
19
+
20
+ query_string.sub!(/^\?/, '')
21
+
22
+ assert_equal ["ids=12345", "metrics=country"], query_string.split('&').sort
23
+ end
24
+
25
+ should "return an empty query string if parameters are empty" do
26
+ data_request = DataRequest.new(@session, "")
27
+ assert_equal "", data_request.query_string
28
+ end
29
+
30
+ should "be able to build a uri" do
31
+ url = 'http://example.com'
32
+ expected = URI.parse('http://example.com')
33
+
34
+ assert_equal expected, DataRequest.new(@session, url).uri
35
+ end
36
+
37
+ should "be able to send a request for a single user" do
38
+ @session.stubs(:single_user?).returns(true)
39
+ response = mock('Net::HTTPOK') do |m|
40
+ m.expects(:kind_of?).with(Net::HTTPSuccess).returns(true)
41
+ end
42
+
43
+ data_request = DataRequest.new(@session, 'https://example.com/data', 'key' => 'value')
44
+ data_request.expects(:single_user_request).with('GoogleLogin').returns(response)
45
+ data_request.send_request
46
+
47
+ assert_received(data_request, :single_user_request)
48
+ end
49
+
50
+ should "be able to send a request for an auth_sub" do
51
+ @session.stubs(:auth_sub?).returns(true)
52
+ response = mock('Net::HTTPOK') do |m|
53
+ m.expects(:kind_of?).with(Net::HTTPSuccess).returns(true)
54
+ end
55
+
56
+ data_request = DataRequest.new(@session, 'https://example.com/data', 'key' => 'value')
57
+ data_request.expects(:single_user_request).with('AuthSub').returns(response)
58
+ data_request.send_request
59
+
60
+ assert_received(data_request, :single_user_request)
61
+ end
62
+
63
+ should "be able to send a request for an oauth user" do
64
+ @session.stubs(:single_user?).returns(false)
65
+ @session.stubs(:oauth_user?).returns(true)
66
+ response = mock('Net::HTTPOK') do |m|
67
+ m.expects(:kind_of?).with(Net::HTTPSuccess).returns(true)
68
+ end
69
+
70
+ data_request = DataRequest.new(@session, 'https://example.com/data', 'key' => 'value')
71
+ data_request.stubs(:oauth_user_request).returns(response)
72
+ data_request.send_request
73
+
74
+ assert_received(data_request, :oauth_user_request)
75
+ end
76
+
77
+ should "raise if the request is unauthorized" do
78
+ @session.stubs(:single_user?).returns(false)
79
+ @session.stubs(:oauth_user?).returns(true)
80
+ response = mock('Net::HTTPUnauthorized', :body => 'Error')
81
+
82
+ data_request = DataRequest.new(@session, 'https://example.com/data', 'key' => 'value')
83
+ data_request.stubs(:oauth_user_request).returns(response)
84
+
85
+ assert_raises(Garb::DataRequest::ClientError) do
86
+ data_request.send_request
87
+ end
88
+ end
89
+
90
+ should "be able to request via the ouath access token" do
91
+ access_token = stub(:get => "responseobject")
92
+ @session.stubs(:access_token).returns(access_token)
93
+
94
+ data_request = DataRequest.new(@session, 'https://example.com/data', 'key' => 'value')
95
+ assert_equal 'responseobject', data_request.oauth_user_request
96
+
97
+ assert_received(@session, :access_token)
98
+ assert_received(access_token, :get) {|e| e.with('https://example.com/data?key=value')}
99
+ end
100
+
101
+ should "be able to request via http with an auth token" do
102
+ @session.expects(:auth_token).with().returns('toke')
103
+ response = mock
104
+
105
+ http = mock do |m|
106
+ m.expects(:use_ssl=).with(true)
107
+ m.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
108
+ m.expects(:get).with('/data?key=value', 'Authorization' => 'GoogleLogin auth=toke').returns(response)
109
+ end
110
+
111
+ Net::HTTP.expects(:new).with('example.com', 443).returns(http)
112
+
113
+ data_request = DataRequest.new(@session, 'https://example.com/data', 'key' => 'value')
114
+ assert_equal response, data_request.single_user_request
115
+ end
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,59 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', '/test_helper')
2
+
3
+ module Garb
4
+ class FilterParametersTest < MiniTest::Unit::TestCase
5
+ def self.should_define_operators(*operators)
6
+ operators.each do |operator|
7
+ should "create an operator and add to parameters for the #{operator} method" do
8
+ new_operator = stub
9
+ symbol = :foo
10
+
11
+ SymbolOperator.expects(:new).with(:bar, operator).returns(new_operator)
12
+ @filter_parameters.filters do
13
+ send(operator.to_sym, :bar, 100)
14
+ end
15
+
16
+ parameter = {new_operator => 100}
17
+ assert_equal parameter, @filter_parameters.parameters.last
18
+ end
19
+ end
20
+ end
21
+
22
+ context "A FilterParameters" do
23
+ setup do
24
+ @filter_parameters = FilterParameters.new
25
+ end
26
+
27
+ should_define_operators :eql, :not_eql, :gt, :gte, :lt, :lte,
28
+ :matches, :does_not_match, :contains, :does_not_contain, :substring, :not_substring
29
+
30
+ should "instance eval for filters" do
31
+ blk = lambda {"in a block"}
32
+
33
+ @filter_parameters.expects(:instance_eval)
34
+ @filter_parameters.filters(&blk)
35
+ end
36
+
37
+ context "when converting parameters hash into query string parameters" do
38
+ should "parameterize hash operators and join elements with AND" do
39
+ @filter_parameters.filters do
40
+ eql(:city, 'New York City')
41
+ eql(:state, 'New York')
42
+ end
43
+
44
+ params = ['ga:city%3D%3DNew+York+City', 'ga:state%3D%3DNew+York']
45
+ assert_equal params, @filter_parameters.to_params['filters'].split(';').sort
46
+ end
47
+
48
+ should "properly encode operators" do
49
+ @filter_parameters.filters do
50
+ contains(:page_path, 'New York')
51
+ end
52
+
53
+ params = {'filters' => 'ga:pagePath%3D~New+York'}
54
+ assert_equal params, @filter_parameters.to_params
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end