gaah 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YTZkNjZhYTVmMjJiMjBiNzMzYWRiOTZjNDFlOTQ0MjQ2OTE5ZDg0YQ==
5
+ data.tar.gz: !binary |-
6
+ ZTUwMmEwN2E1MDVjNzNlMTdiOTNiZDA3YTdmOWZiOTAxMTAwNWFhYQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NTc5OTQwNGY3MzU0MzVjYzljY2E1ZDNiZWIyOGU3ZGI4ZGU1ZDhiZmRjMTdl
10
+ ZmJmNGY2ZDI2ZDRjNDU1YjAyMTIzM2M2ZDY0NTg1ZjMyMTQzZTI2MzY1NmUx
11
+ M2I5NmQ3MmFmZmJhOTY1MmI3NTQ5MTA4MjY4MmUyZDI2MzY3ODM=
12
+ data.tar.gz: !binary |-
13
+ MDNlYzRjYWI0ZWU1M2U3YjU5NjdhOTA5ZGQwNzI3ZGNiNjk1Y2IwODc3NDEz
14
+ YzI0YWMxOTIxZDZmMTQ4ZTYxZmI0ODIzODlmNjlmYWYxNWNiYjJiNWJkOGJi
15
+ ZTg3ZDk3MTZmOGI5NjM0YmMwMDU4NjVlMmRlOGUyNTk2OTFiYTk=
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'nokogiri', "~> 1.5"
4
+ gem 'oauth', "~> 0.4"
5
+ gem 'queryparams', "0.0.3"
6
+ gem 'rspec', "~> 2.5"
data/Gemfile.lock ADDED
@@ -0,0 +1,24 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.2.2)
5
+ nokogiri (1.5.9)
6
+ oauth (0.4.7)
7
+ queryparams (0.0.3)
8
+ rspec (2.13.0)
9
+ rspec-core (~> 2.13.0)
10
+ rspec-expectations (~> 2.13.0)
11
+ rspec-mocks (~> 2.13.0)
12
+ rspec-core (2.13.1)
13
+ rspec-expectations (2.13.0)
14
+ diff-lcs (>= 1.1.3, < 2.0)
15
+ rspec-mocks (2.13.0)
16
+
17
+ PLATFORMS
18
+ ruby
19
+
20
+ DEPENDENCIES
21
+ nokogiri (~> 1.5)
22
+ oauth (~> 0.4)
23
+ queryparams (= 0.0.3)
24
+ rspec (~> 2.5)
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # GAAH - Google Apps API Helper
2
+
3
+ ### API Wrapper for Google Apps Marketplace
4
+
5
+ Currently supports:
6
+
7
+ ![](https://developers.google.com/gdata/images/service_icons/gdata-contacts.png) Provisioning API (read-only)
8
+
9
+ ![](https://developers.google.com/gdata/images/service_icons/gdata-calendar.png) Calendar API
10
+
11
+ ### Example Code
12
+
13
+ ```ruby
14
+ CLIENT_KEY = '000000000000.apps.googleusercontent.com'
15
+ CLIENT_SECRET = 'abcdefghijklmnopqrstuvwx'
16
+ DOMAIN = 'example.com'
17
+
18
+ # Setup
19
+ require 'gaah'
20
+ Gaah.setup_oauth(CLIENT_KEY, CLIENT_SECRET)
21
+ Gaah.domain = DOMAIN
22
+
23
+ # Get users
24
+ users = Gaah::Provisioning::Api.users
25
+ user = users.first
26
+ user.id # "https://apps-apis.google.com/a/feeds/example.com/user/2.0/bobert"
27
+ user.name # "Bobert Jones"
28
+ user.title # "bobert"
29
+
30
+ # Get calendar events
31
+ xoauth_requestor_id = 'me@example.com'
32
+ events = user.events(xoauth_requestor_id)
33
+ event = events.first
34
+ event.title # "Meeting with Joe"
35
+ event.when.start_time # 2013-04-16 13:00:00 -0400
36
+ ```
37
+
38
+ ### Resources
39
+
40
+ Warning: Google documentation links sometimes move without good redirection.
41
+
42
+ * [Google Data APIs](https://developers.google.com/gdata/)
43
+ * [Google Apps Application APIs](https://developers.google.com/google-apps/app-apis)
44
+ * [Google Apps Administrative APIs](https://developers.google.com/google-apps/admin-apis)
45
+ * [Google Calendar API v2](https://developers.google.com/google-apps/calendar/v2/developers_guide_protocol)
46
+ * [Google Apps Provisioning API v2](https://developers.google.com/google-apps/provisioning/)
47
+
48
+ ### Todo
49
+ * Look into data api v3 when Provisioning API is updated
50
+ * Possibly integrate [Google Apps Calendar Resource API](https://developers.google.com/google-apps/calendar-resource/)
51
+ * Error handling
data/gaah.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "gaah/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "gaah"
7
+ s.version = Gaah::VERSION
8
+ s.date = "2013-04-03"
9
+ s.authors = ["Hwan-Joon Choi"]
10
+ s.email = ["hc5duke@gmail.com"]
11
+ s.homepage = "https://github.com/distill-inc/gaah"
12
+
13
+ s.summary = "Limited API Wrapper for Google Apps API."
14
+ s.description = "Google Apps Marketplace API wrapper in ruby for Distill."
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency "nokogiri", "~> 1.5"
22
+ s.add_dependency 'oauth', "~> 0.4"
23
+ s.add_dependency 'queryparams', "0.0.3"
24
+ s.add_development_dependency "rspec", "~> 2.5"
25
+ end
data/lib/gaah.rb ADDED
@@ -0,0 +1 @@
1
+ require 'gaah/base'
@@ -0,0 +1,49 @@
1
+ module Gaah
2
+ class ApiClient
3
+ class << self
4
+ attr_accessor :consumer_key, :consumer_secret, :instance
5
+
6
+ def setup_oauth(key, secret)
7
+ @@consumer_key = key
8
+ @@consumer_secret = secret
9
+ true
10
+ end
11
+
12
+ def not_setup?
13
+ @@consumer_key.nil? || @@consumer_secret.nil?
14
+ end
15
+
16
+ def instance
17
+ @@instance ||= new
18
+ end
19
+ end
20
+
21
+ def initialize
22
+ raise 'consumer_key and consumer_secret not set' if ApiClient.not_setup?
23
+ oauth_consumer = OAuth::Consumer.new(@@consumer_key, @@consumer_secret)
24
+ @token = OAuth::AccessToken.new(oauth_consumer)
25
+ end
26
+
27
+ def get(base, query_params={})
28
+ url = base
29
+ url = "#{base}?#{QueryParams.encode(query_params)}" if query_params.keys.length > 0
30
+ make_request(:get, url)
31
+ end
32
+
33
+ private
34
+
35
+ def make_request(method, url)
36
+ response = @token.request(method, url, 'GData-Version' => '2.0')
37
+
38
+ if response.is_a? Net::HTTPSuccess
39
+ response.body
40
+ elsif response.is_a? Net::HTTPFound
41
+ url = response['Location']
42
+ make_request(method, response['Location'])
43
+ else
44
+ # TODO: error handling
45
+ response
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,40 @@
1
+ module Gaah
2
+ class ApiModel
3
+ attr_reader :id
4
+
5
+ def self.batch_create(xml)
6
+ (xml/:entry).map(&method(:new))
7
+ end
8
+
9
+ protected
10
+
11
+ def ==(other)
12
+ @id == other.id
13
+ end
14
+
15
+ private
16
+
17
+ def store_xml(xml)
18
+ @xml = xml.is_a?(String) ? Nokogiri::XML(xml) : xml
19
+ end
20
+
21
+ def inner_text(tag)
22
+ (@xml/tag).inner_text
23
+ end
24
+
25
+ def attr_value(tag, attribute = 'value')
26
+ node = (@xml/tag).first
27
+ return nil if node.nil?
28
+ attr = node.attr(attribute)
29
+ attr.respond_to?(:value) ? attr.value : attr
30
+ end
31
+
32
+ def tag_value(tag)
33
+ attr_value(tag).split('.').last
34
+ end
35
+
36
+ def inspect
37
+ "#<#{self.class}:#{object_id}>"
38
+ end
39
+ end
40
+ end
data/lib/gaah/base.rb ADDED
@@ -0,0 +1,22 @@
1
+ require 'cgi'
2
+ require 'time'
3
+ require 'date'
4
+ require 'json'
5
+
6
+ require 'oauth'
7
+ require 'nokogiri'
8
+ require 'queryparams'
9
+
10
+ require 'gaah/api_client'
11
+ require 'gaah/api_model'
12
+ require 'gaah/calendar/api'
13
+ require 'gaah/provisioning/api'
14
+
15
+ module Gaah
16
+ class << self
17
+ attr_accessor :domain
18
+ def setup_oauth(key, secret)
19
+ ApiClient.setup_oauth(key, secret)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,41 @@
1
+ require 'gaah/calendar/event'
2
+ require 'gaah/calendar/when'
3
+ require 'gaah/calendar/who'
4
+
5
+ module Gaah
6
+ module Calendar
7
+ class Api
8
+ class << self
9
+ def events(xoauth_requestor_id, options)
10
+ url = build_api_url(options[:email])
11
+ params = build_api_params(xoauth_requestor_id, options)
12
+ xml = ApiClient.instance.get(url, params)
13
+ events = Nokogiri::XML(xml)/:entry
14
+ Event.batch_create(events)
15
+ end
16
+
17
+ private
18
+
19
+ def build_api_url(email)
20
+ API_URL.sub('EMAIL', email || 'default')
21
+ end
22
+
23
+ def build_api_params(xoauth_requestor_id, options)
24
+ api_params = { xoauth_requestor_id: xoauth_requestor_id }
25
+ api_params[:orderby] = options.delete(:order_by) || 'starttime'
26
+ api_params[:singleevents] = options.delete(:single_events) || true
27
+ api_params[:sortorder] = options.delete(:sort_order) || 'a'
28
+ api_params['start-min'] = stringify(options.delete(:start_min))
29
+ api_params['start-max'] = stringify(options.delete(:start_max))
30
+ api_params
31
+ end
32
+
33
+ def stringify(time)
34
+ time.nil? ? nil : time.strftime('%Y-%m-%dT17:00:00')
35
+ end
36
+
37
+ API_URL = 'https://www.google.com/calendar/feeds/EMAIL/private/full'
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,58 @@
1
+ module Gaah
2
+ module Calendar
3
+ class Event < Gaah::ApiModel
4
+ attr_reader :published, :updated, :title, :content, :status, :who, :when, :where, :author, :transparency, :visibility
5
+
6
+ def initialize(xml)
7
+ store_xml(xml)
8
+
9
+ @id = inner_text(:id)
10
+ @published = Time.parse(inner_text(:published))
11
+ @updated = Time.parse(inner_text(:updated))
12
+ @title = inner_text(:title)
13
+ @content = inner_text(:content)
14
+ @status = tag_value('gd|eventStatus')
15
+ @where = attr_value('gd|where', 'valueString')
16
+ @author = Who.new((@xml/:author).first)
17
+ @when = parse_when
18
+ @who = parse_who
19
+ @transparency = tag_value('gd|transparency')
20
+ @visibility = tag_value('gd|visibility')
21
+ end
22
+
23
+ def to_json(*args)
24
+ {
25
+ id: @id,
26
+ published: @published,
27
+ updated: @updated,
28
+ title: @title,
29
+ content: @content,
30
+ status: @status,
31
+ where: @where,
32
+ author: @author,
33
+ when: @when,
34
+ who: @who,
35
+ transparency: @transparency,
36
+ visibility: @visibility,
37
+ }.to_json
38
+ end
39
+
40
+ private
41
+
42
+ def store_xml(xml)
43
+ super
44
+ unless @xml.attr('gd:kind') == "calendar#event"
45
+ puts "Possible invalid event xml - gd:kind is #{ @xml.attr('gd:kind') }"
46
+ end
47
+ end
48
+
49
+ def parse_when
50
+ When.new(attr_value('> gd|when', 'startTime'), attr_value('> gd|when', 'endTime'))
51
+ end
52
+
53
+ def parse_who
54
+ (@xml/'gd|who').map {|attendee| Who.new(attendee) }
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,18 @@
1
+ module Gaah
2
+ module Calendar
3
+ class When
4
+ attr_accessor :start_time, :end_time
5
+ def initialize(start_time, end_time)
6
+ @start_time = Time.parse(start_time)
7
+ @end_time = Time.parse(end_time)
8
+ end
9
+
10
+ def to_json(*args)
11
+ {
12
+ start_time: @start_time,
13
+ end_time: @end_time,
14
+ }.to_json
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,51 @@
1
+ module Gaah
2
+ module Calendar
3
+ class Who
4
+ attr_reader :name, :email, :aliases
5
+ def initialize(xml)
6
+ @aliases = []
7
+
8
+ case xml.name
9
+ when 'author'
10
+ @name = (xml/:name).inner_text
11
+ @email = (xml/:email).inner_text
12
+ when 'who'
13
+ @name = xml.attr(:valueString)
14
+ @email = xml.attr(:email)
15
+ when 'entry'
16
+ @name = (xml/:title).inner_text
17
+ (xml/'gd|email').each do |gd_email|
18
+ email = gd_email.attr('address')
19
+ @aliases << email
20
+ @email = email if gd_email.attr('primary') == 'true'
21
+ end
22
+ else
23
+ raise "Invalid person record #{xml.name}"
24
+ end
25
+ end
26
+
27
+ def catch_all_user?
28
+ self.email.to_s =~ /^\.@/
29
+ end
30
+
31
+ def to_json(*args)
32
+ {
33
+ name: name,
34
+ email: email,
35
+ aliases: aliases,
36
+ }.to_json
37
+ end
38
+
39
+ def self.batch_process(xml, exclude_catch_all_user = true)
40
+ (xml/:entry).map do |entry|
41
+ who = new(entry)
42
+ if exclude_catch_all_user && who.catch_all_user?
43
+ nil
44
+ else
45
+ who
46
+ end
47
+ end.compact
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,16 @@
1
+ require 'gaah/provisioning/user'
2
+
3
+ module Gaah
4
+ module Provisioning
5
+ class Api
6
+ class << self
7
+ def users
8
+ url = "https://apps-apis.google.com/a/feeds/#{Gaah.domain}/user/2.0"
9
+ xml = ApiClient.instance.get(url)
10
+ users = Nokogiri::XML(xml)/:entry
11
+ User.batch_create(users)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,53 @@
1
+ module Gaah
2
+ module Provisioning
3
+ class User < Gaah::ApiModel
4
+ attr_reader :suspended, :admin, :title, :user_name, :family_name, :given_name, :name, :email
5
+
6
+ def initialize(entry)
7
+ store_xml(entry)
8
+
9
+ # Meta
10
+ @id = inner_text(:id)
11
+ @email = parse_email
12
+ @suspended = attr_value('apps|login', 'suspended') == 'true'
13
+ @admin = attr_value('apps|login', 'admin') == 'true'
14
+
15
+ # Name
16
+ @title = inner_text(:title)
17
+ @user_name = attr_value('apps|login', 'userName')
18
+ @family_name = attr_value('apps|name', 'familyName')
19
+ @given_name = attr_value('apps|name', 'givenName')
20
+ end
21
+
22
+ def parse_email
23
+ email_link = (@xml/'gd|feedLink').select { |link|
24
+ link.attr('rel').end_with?('user.emailLists')
25
+ }.first
26
+ return nil if email_link.nil?
27
+ CGI::unescape(email_link.attr('href').split('recipient=').last)
28
+ end
29
+
30
+ def name
31
+ "#{@given_name} #{@family_name}"
32
+ end
33
+
34
+ def events(xoauth_requestor_id, options = {})
35
+ options[:email] = @email
36
+ Gaah::Calendar::Api.events(xoauth_requestor_id, options)
37
+ end
38
+
39
+ def to_json(*args)
40
+ {
41
+ id: @id,
42
+ suspended: @suspended,
43
+ admin: @admin,
44
+ title: @title,
45
+ user_name: @user_name,
46
+ family_name: @family_name,
47
+ given_name: @given_name,
48
+ name: @name,
49
+ }.to_json
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ module Gaah
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,369 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <feed xmlns="http://www.w3.org/2005/Atom" xmlns:gCal="http://schemas.google.com/gCal/2005" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:gd="http://schemas.google.com/g/2005" gd:etag="W/&quot;CU4CQn47eCp7JGA9WhBXGUg.&quot;" gd:kind="calendar#eventFeed">
3
+ <id>http://www.google.com/calendar/feeds/bobert%40example.com/private/full</id>
4
+ <updated>2013-04-03T01:52:43.000Z</updated>
5
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/g/2005#event"/>
6
+ <title>bobert@example.com</title>
7
+ <subtitle>bobert@example.com</subtitle>
8
+ <link rel="alternate" type="text/html" href="https://www.google.com/calendar/embed?src=bobert%40example.com"/>
9
+ <link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="https://www.google.com/calendar/feeds/bobert%40example.com/private/full?xoauth_requestor_id=bobert@example.com"/>
10
+ <link rel="http://schemas.google.com/g/2005#batch" type="application/atom+xml" href="https://www.google.com/calendar/feeds/bobert%40example.com/private/full/batch?xoauth_requestor_id=bobert@example.com"/>
11
+ <link rel="self" type="application/atom+xml" href="https://www.google.com/calendar/feeds/bobert%40example.com/private/full?max-results=25&amp;start-min=2013-03-31T17%3A00%3A00&amp;singleevents=true&amp;start-max=2013-04-07T17%3A00%3A00&amp;sortorder=a&amp;orderby=starttime&amp;xoauth_requestor_id=bobert@example.com"/>
12
+ <author>
13
+ <name>Bobert Jones</name>
14
+ <email>bobert@example.com</email>
15
+ </author>
16
+ <generator version="1.0" uri="http://www.google.com/calendar">Google Calendar</generator>
17
+ <openSearch:totalResults>10</openSearch:totalResults>
18
+ <openSearch:startIndex>1</openSearch:startIndex>
19
+ <openSearch:itemsPerPage>25</openSearch:itemsPerPage>
20
+ <gCal:timezone value="America/Los_Angeles"/>
21
+ <gCal:timesCleaned value="0"/>
22
+ <entry gd:etag="&quot;GUQLQwVIfip7JGA6WhJa&quot;" gd:kind="calendar#event">
23
+ <id>http://www.google.com/calendar/feeds/bobert%40example.com/events/n9ommrehhjm5dc6q89ofkm3f8g</id>
24
+ <published>2013-03-25T16:49:48.000Z</published>
25
+ <updated>2013-03-25T17:03:10.000Z</updated>
26
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/g/2005#event"/>
27
+ <title>MomCorp</title>
28
+ <content/>
29
+ <link rel="alternate" type="text/html" href="https://www.google.com/calendar/event?eid=bjlvbW1yZWhoam01ZGM2cTg5b2ZrbTNmOGcga2FpQGRpc3RpbGwuY2M" title="alternate"/>
30
+ <link rel="self" type="application/atom+xml" href="https://www.google.com/calendar/feeds/bobert%40example.com/private/full/n9ommrehhjm5dc6q89ofkm3f8g?xoauth_requestor_id=bobert@example.com"/>
31
+ <author>
32
+ <name>Bobert Jones</name>
33
+ <email>bobert@example.com</email>
34
+ </author>
35
+ <gd:comments>
36
+ <gd:feedLink href="https://www.google.com"/>
37
+ </gd:comments>
38
+ <gd:eventStatus value="http://schemas.google.com/g/2005#event.confirmed"/>
39
+ <gd:where valueString=""/>
40
+ <gd:who email="bobert@example.com" rel="http://schemas.google.com/g/2005#event.organizer" valueString="Bobert Jones">
41
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.accepted"/>
42
+ </gd:who>
43
+ <gd:who email="fry@example.com" rel="http://schemas.google.com/g/2005#event.attendee" valueString="Philip Fry">
44
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.accepted"/>
45
+ </gd:who>
46
+ <gd:when endTime="2013-04-01T11:00:00.000-07:00" startTime="2013-04-01T10:00:00.000-07:00"/>
47
+ <gd:transparency value="http://schemas.google.com/g/2005#event.opaque"/>
48
+ <gd:visibility value="http://schemas.google.com/g/2005#event.default"/>
49
+ <gCal:anyoneCanAddSelf value="false"/>
50
+ <gCal:guestsCanInviteOthers value="true"/>
51
+ <gCal:guestsCanModify value="false"/>
52
+ <gCal:guestsCanSeeGuests value="true"/>
53
+ <gCal:sequence value="0"/>
54
+ <gCal:uid value="n9ommrehhjm5dc6q89ofkm3f8g@google.com"/>
55
+ </entry>
56
+ <entry gd:etag="&quot;EEgITgRIdip7JGA6WhNT&quot;" gd:kind="calendar#event">
57
+ <id>http://www.google.com/calendar/feeds/bobert%40example.com/events/b2if8georrqg4ie9p04t3jdvd0_20130401T233000Z</id>
58
+ <published>2013-03-04T16:52:10.000Z</published>
59
+ <updated>2013-04-01T19:51:38.000Z</updated>
60
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/g/2005#event"/>
61
+ <title>Weekly Sync</title>
62
+ <content/>
63
+ <link rel="alternate" type="text/html" href="https://www.google.com/calendar/event?eid=YjJpZjhnZW9ycnFnNGllOXAwNHQzamR2ZDBfMjAxMzA0MDFUMjMzMDAwWiBrYWlAZGlzdGlsbC5jYw" title="alternate"/>
64
+ <link rel="self" type="application/atom+xml" href="https://www.google.com/calendar/feeds/bobert%40example.com/private/full/b2if8georrqg4ie9p04t3jdvd0_20130401T233000Z?xoauth_requestor_id=bobert@example.com"/>
65
+ <author>
66
+ <name>Bobert Jones</name>
67
+ <email>bobert@example.com</email>
68
+ </author>
69
+ <gd:comments>
70
+ <gd:feedLink href="https://www.google.com"/>
71
+ </gd:comments>
72
+ <gd:eventStatus value="http://schemas.google.com/g/2005#event.confirmed"/>
73
+ <gd:where valueString=""/>
74
+ <gd:originalEvent href="http://www.google.com/calendar/feeds/bobert%40example.com/private/full/b2if8georrqg4ie9p04t3jdvd0" id="b2if8georrqg4ie9p04t3jdvd0">
75
+ <gd:when startTime="2013-04-01T16:30:00.000-07:00"/>
76
+ </gd:originalEvent>
77
+ <gd:who email="bobert@example.com" rel="http://schemas.google.com/g/2005#event.organizer" valueString="Bobert Jones">
78
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.accepted"/>
79
+ </gd:who>
80
+ <gd:who email="fry@example.com" rel="http://schemas.google.com/g/2005#event.attendee" valueString="Philip Fry">
81
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.accepted"/>
82
+ </gd:who>
83
+ <gd:who email="leela@example.com" rel="http://schemas.google.com/g/2005#event.attendee" valueString="Turanga Leela">
84
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.accepted"/>
85
+ </gd:who>
86
+ <gd:who email="bender@example.com" rel="http://schemas.google.com/g/2005#event.attendee" valueString="Bender Rodriguez">
87
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.invited"/>
88
+ </gd:who>
89
+ <gd:who email="farnsworth@example.com" rel="http://schemas.google.com/g/2005#event.attendee" valueString="Hubert Farnsworth">
90
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.accepted"/>
91
+ </gd:who>
92
+ <gd:who email="zoidberg@example.com" rel="http://schemas.google.com/g/2005#event.attendee" valueString="John Zoidberg">
93
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.invited"/>
94
+ </gd:who>
95
+ <gd:when endTime="2013-04-01T13:00:00.000-07:00" startTime="2013-04-01T12:00:00.000-07:00"/>
96
+ <gd:transparency value="http://schemas.google.com/g/2005#event.opaque"/>
97
+ <gd:visibility value="http://schemas.google.com/g/2005#event.default"/>
98
+ <gCal:anyoneCanAddSelf value="false"/>
99
+ <gCal:guestsCanInviteOthers value="true"/>
100
+ <gCal:guestsCanModify value="true"/>
101
+ <gCal:guestsCanSeeGuests value="true"/>
102
+ <gCal:sequence value="1"/>
103
+ <gCal:uid value="b2if8georrqg4ie9p04t3jdvd0@google.com"/>
104
+ </entry>
105
+ <entry gd:etag="&quot;EE8IQQdHfyp7JGA6WhNT&quot;" gd:kind="calendar#event">
106
+ <id>http://www.google.com/calendar/feeds/bobert%40example.com/events/8ac5lhpq27gockkrh8ncsjbrq8</id>
107
+ <published>2013-03-29T00:33:26.000Z</published>
108
+ <updated>2013-03-29T07:46:01.000Z</updated>
109
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/g/2005#event"/>
110
+ <title>Mars University</title>
111
+ <content/>
112
+ <link rel="alternate" type="text/html" href="https://www.google.com/calendar/event?eid=OGFjNWxocHEyN2dvY2trcmg4bmNzamJycTgga2FpQGRpc3RpbGwuY2M" title="alternate"/>
113
+ <link rel="self" type="application/atom+xml" href="https://www.google.com/calendar/feeds/bobert%40example.com/private/full/8ac5lhpq27gockkrh8ncsjbrq8?xoauth_requestor_id=bobert@example.com"/>
114
+ <author>
115
+ <name>Bobert Jones</name>
116
+ <email>bobert@example.com</email>
117
+ </author>
118
+ <gd:comments>
119
+ <gd:feedLink href="https://www.google.com"/>
120
+ </gd:comments>
121
+ <gd:eventStatus value="http://schemas.google.com/g/2005#event.confirmed"/>
122
+ <gd:where valueString=""/>
123
+ <gd:who email="bobert@example.com" rel="http://schemas.google.com/g/2005#event.organizer" valueString="Bobert Jones">
124
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.accepted"/>
125
+ </gd:who>
126
+ <gd:who email="fry@example.com" rel="http://schemas.google.com/g/2005#event.attendee" valueString="Philip Fry">
127
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.accepted"/>
128
+ </gd:who>
129
+ <gd:when endTime="2013-04-01T15:00:00.000-07:00" startTime="2013-04-01T14:00:00.000-07:00"/>
130
+ <gd:transparency value="http://schemas.google.com/g/2005#event.opaque"/>
131
+ <gd:visibility value="http://schemas.google.com/g/2005#event.default"/>
132
+ <gCal:anyoneCanAddSelf value="false"/>
133
+ <gCal:guestsCanInviteOthers value="true"/>
134
+ <gCal:guestsCanModify value="false"/>
135
+ <gCal:guestsCanSeeGuests value="true"/>
136
+ <gCal:sequence value="0"/>
137
+ <gCal:uid value="8ac5lhpq27gockkrh8ncsjbrq8@google.com"/>
138
+ </entry>
139
+ <entry gd:etag="&quot;GU4DTgRGdyp7JGA6WhJa&quot;" gd:kind="calendar#event">
140
+ <id>http://www.google.com/calendar/feeds/bobert%40example.com/events/_6t332c2464ojeb9m6p23gb9k8krjcb9o6ko3iba668q38cpm64s46ga68c</id>
141
+ <published>2013-03-19T17:57:11.000Z</published>
142
+ <updated>2013-03-19T17:57:59.000Z</updated>
143
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/g/2005#event"/>
144
+ <title>Block for strategy session</title>
145
+ <content>Palo Alto</content>
146
+ <link rel="alternate" type="text/html" href="https://www.google.com/calendar/event?eid=XzZ0MzMyYzI0NjRvamViOW02cDIzZ2I5azhrcmpjYjlvNmtvM2liYTY2OHEzOGNwbTY0czQ2Z2E2OGMga2FpQGRpc3RpbGwuY2M" title="alternate"/>
147
+ <link rel="self" type="application/atom+xml" href="https://www.google.com/calendar/feeds/bobert%40example.com/private/full/_6t332c2464ojeb9m6p23gb9k8krjcb9o6ko3iba668q38cpm64s46ga68c?xoauth_requestor_id=bobert@example.com"/>
148
+ <author>
149
+ <name>Bobert Jones</name>
150
+ <email>bobert@example.com</email>
151
+ </author>
152
+ <gd:comments>
153
+ <gd:feedLink href="https://www.google.com"/>
154
+ </gd:comments>
155
+ <gd:eventStatus value="http://schemas.google.com/g/2005#event.confirmed"/>
156
+ <gd:where valueString="530 Lytton Ave Suite 305 Palo Alto"/>
157
+ <gd:who email="amy@example.com" rel="http://schemas.google.com/g/2005#event.attendee" valueString="Amy Wong">
158
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.invited"/>
159
+ </gd:who>
160
+ <gd:who email="bender@example.com" rel="http://schemas.google.com/g/2005#event.attendee" valueString="Bender Rodriguez">
161
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.invited"/>
162
+ </gd:who>
163
+ <gd:who email="bobert@example.com" rel="http://schemas.google.com/g/2005#event.attendee" valueString="Bobert Jones">
164
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.accepted"/>
165
+ </gd:who>
166
+ <gd:who email="hermes@example.com" rel="http://schemas.google.com/g/2005#event.organizer" valueString="hermes@examle.com"/>
167
+ <gd:when endTime="2013-04-02T12:30:00.000-07:00" startTime="2013-04-02T11:00:00.000-07:00"/>
168
+ <gd:transparency value="http://schemas.google.com/g/2005#event.opaque"/>
169
+ <gd:visibility value="http://schemas.google.com/g/2005#event.default"/>
170
+ <gCal:anyoneCanAddSelf value="false"/>
171
+ <gCal:guestsCanInviteOthers value="false"/>
172
+ <gCal:guestsCanModify value="false"/>
173
+ <gCal:guestsCanSeeGuests value="true"/>
174
+ <gCal:privateCopy value="true"/>
175
+ <gCal:sequence value="0"/>
176
+ <gCal:uid value="7F10D117-66D8-4E76-8509-F2443618CAFC"/>
177
+ </entry>
178
+ <entry gd:etag="&quot;EEsLTg1Fdyp7JGA6WhNT&quot;" gd:kind="calendar#event">
179
+ <id>http://www.google.com/calendar/feeds/bobert%40example.com/events/c55b0pe0odp94803fgv803c22c</id>
180
+ <published>2013-04-02T21:05:49.000Z</published>
181
+ <updated>2013-04-02T21:05:49.000Z</updated>
182
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/g/2005#event"/>
183
+ <title>D.O.O.P.</title>
184
+ <content/>
185
+ <link rel="alternate" type="text/html" href="https://www.google.com/calendar/event?eid=YzU1YjBwZTBvZHA5NDgwM2ZndjgwM2MyMmMga2FpQGRpc3RpbGwuY2M" title="alternate"/>
186
+ <link rel="self" type="application/atom+xml" href="https://www.google.com/calendar/feeds/bobert%40example.com/private/full/c55b0pe0odp94803fgv803c22c?xoauth_requestor_id=bobert@example.com"/>
187
+ <author>
188
+ <name>Bobert Jones</name>
189
+ <email>bobert@example.com</email>
190
+ </author>
191
+ <gd:comments>
192
+ <gd:feedLink href="https://www.google.com"/>
193
+ </gd:comments>
194
+ <gd:eventStatus value="http://schemas.google.com/g/2005#event.confirmed"/>
195
+ <gd:where valueString=""/>
196
+ <gd:who email="bobert@example.com" rel="http://schemas.google.com/g/2005#event.organizer" valueString="bobert@example.com"/>
197
+ <gd:when endTime="2013-04-02T15:00:00.000-07:00" startTime="2013-04-02T14:30:00.000-07:00"/>
198
+ <gd:transparency value="http://schemas.google.com/g/2005#event.opaque"/>
199
+ <gd:visibility value="http://schemas.google.com/g/2005#event.default"/>
200
+ <gCal:anyoneCanAddSelf value="false"/>
201
+ <gCal:guestsCanInviteOthers value="true"/>
202
+ <gCal:guestsCanModify value="false"/>
203
+ <gCal:guestsCanSeeGuests value="true"/>
204
+ <gCal:sequence value="0"/>
205
+ <gCal:uid value="c55b0pe0odp94803fgv803c22c@google.com"/>
206
+ </entry>
207
+ <entry gd:etag="&quot;EEgJQQFJeCp7JGA6WhNT&quot;" gd:kind="calendar#event">
208
+ <id>http://www.google.com/calendar/feeds/bobert%40example.com/events/d0fq0gul9ml5t7i6oeiii37ql4</id>
209
+ <published>2013-04-01T21:53:55.000Z</published>
210
+ <updated>2013-04-01T21:56:26.000Z</updated>
211
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/g/2005#event"/>
212
+ <title>Head Museum</title>
213
+ <content/>
214
+ <link rel="alternate" type="text/html" href="https://www.google.com/calendar/event?eid=ZDBmcTBndWw5bWw1dDdpNm9laWlpMzdxbDQga2FpQGRpc3RpbGwuY2M" title="alternate"/>
215
+ <link rel="self" type="application/atom+xml" href="https://www.google.com/calendar/feeds/bobert%40example.com/private/full/d0fq0gul9ml5t7i6oeiii37ql4?xoauth_requestor_id=bobert@example.com"/>
216
+ <author>
217
+ <name>Bobert Jones</name>
218
+ <email>bobert@example.com</email>
219
+ </author>
220
+ <gd:comments>
221
+ <gd:feedLink href="https://www.google.com"/>
222
+ </gd:comments>
223
+ <gd:eventStatus value="http://schemas.google.com/g/2005#event.confirmed"/>
224
+ <gd:where valueString=""/>
225
+ <gd:who email="bobert@example.com" rel="http://schemas.google.com/g/2005#event.organizer" valueString="Bobert Jones">
226
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.accepted"/>
227
+ </gd:who>
228
+ <gd:who email="fry@example.com" rel="http://schemas.google.com/g/2005#event.attendee" valueString="Philip Fry">
229
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.accepted"/>
230
+ </gd:who>
231
+ <gd:when endTime="2013-04-03T14:00:00.000-07:00" startTime="2013-04-03T13:00:00.000-07:00"/>
232
+ <gd:transparency value="http://schemas.google.com/g/2005#event.opaque"/>
233
+ <gd:visibility value="http://schemas.google.com/g/2005#event.default"/>
234
+ <gCal:anyoneCanAddSelf value="false"/>
235
+ <gCal:guestsCanInviteOthers value="true"/>
236
+ <gCal:guestsCanModify value="false"/>
237
+ <gCal:guestsCanSeeGuests value="true"/>
238
+ <gCal:sequence value="0"/>
239
+ <gCal:uid value="d0fq0gul9ml5t7i6oeiii37ql4@google.com"/>
240
+ </entry>
241
+ <entry gd:etag="&quot;EEgJQQJIeCp7JGA6WhNT&quot;" gd:kind="calendar#event">
242
+ <id>http://www.google.com/calendar/feeds/bobert%40example.com/events/m0g74buir5cj3004qc0l393lto</id>
243
+ <published>2013-03-14T23:12:43.000Z</published>
244
+ <updated>2013-04-01T21:58:16.000Z</updated>
245
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/g/2005#event"/>
246
+ <title>Omicron Persei 8</title>
247
+ <content/>
248
+ <link rel="alternate" type="text/html" href="https://www.google.com/calendar/event?eid=bTBnNzRidWlyNWNqMzAwNHFjMGwzOTNsdG8ga2FpQGRpc3RpbGwuY2M" title="alternate"/>
249
+ <link rel="self" type="application/atom+xml" href="https://www.google.com/calendar/feeds/bobert%40example.com/private/full/m0g74buir5cj3004qc0l393lto?xoauth_requestor_id=bobert@example.com"/>
250
+ <author>
251
+ <name>Bobert Jones</name>
252
+ <email>bobert@example.com</email>
253
+ </author>
254
+ <gd:comments>
255
+ <gd:feedLink href="https://www.google.com"/>
256
+ </gd:comments>
257
+ <gd:eventStatus value="http://schemas.google.com/g/2005#event.confirmed"/>
258
+ <gd:where valueString="Planet Omicron Persei 8 "/>
259
+ <gd:who email="bobert@example.com" rel="http://schemas.google.com/g/2005#event.organizer" valueString="Bobert Jones">
260
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.accepted"/>
261
+ </gd:who>
262
+ <gd:who email="fry@example.com" rel="http://schemas.google.com/g/2005#event.attendee" valueString="Philip Fry">
263
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.accepted"/>
264
+ </gd:who>
265
+ <gd:when endTime="2013-04-03T16:00:00.000-07:00" startTime="2013-04-03T15:00:00.000-07:00"/>
266
+ <gd:transparency value="http://schemas.google.com/g/2005#event.opaque"/>
267
+ <gd:visibility value="http://schemas.google.com/g/2005#event.default"/>
268
+ <gCal:anyoneCanAddSelf value="false"/>
269
+ <gCal:guestsCanInviteOthers value="true"/>
270
+ <gCal:guestsCanModify value="false"/>
271
+ <gCal:guestsCanSeeGuests value="true"/>
272
+ <gCal:sequence value="1"/>
273
+ <gCal:uid value="m0g74buir5cj3004qc0l393lto@google.com"/>
274
+ </entry>
275
+ <entry gd:etag="&quot;GU4LRABJfCp7JGA6WhJa&quot;" gd:kind="calendar#event">
276
+ <id>http://www.google.com/calendar/feeds/bobert%40example.com/events/_60q30c1g60o30e1i60o4ac1g60rj8gpl88rj2c1h84s34h9g60s30c1g60o30c1g6so32ghp8h33egi568pk6h9g64o30c1g60o30c1g60o30c1g60o32c1g60o30c1g8h23cc2388ojida36t2jgg9k610k6ga48p2j6cho6grkcdhm84qg</id>
277
+ <published>2013-03-18T16:57:14.000Z</published>
278
+ <updated>2013-03-18T18:11:22.000Z</updated>
279
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/g/2005#event"/>
280
+ <title>Meeting with Lrrr</title>
281
+ <content>password: example</content>
282
+ <link rel="alternate" type="text/html" href="https://www.google.com/calendar/event?eid=XzYwcTMwYzFnNjBvMzBlMWk2MG80YWMxZzYwcmo4Z3BsODhyajJjMWg4NHMzNGg5ZzYwczMwYzFnNjBvMzBjMWc2c28zMmdocDhoMzNlZ2k1NjhwazZoOWc2NG8zMGMxZzYwbzMwYzFnNjBvMzBjMWc2MG8zMmMxZzYwbzMwYzFnOGgyM2NjMjM4OG9qaWRhMzZ0MmpnZzlrNjEwazZnYTQ4cDJqNmNobzZncmtjZGhtODRxZyBrYWlAZGlzdGlsbC5jYw" title="alternate"/>
283
+ <link rel="self" type="application/atom+xml" href="https://www.google.com/calendar/feeds/bobert%40example.com/private/full/_60q30c1g60o30e1i60o4ac1g60rj8gpl88rj2c1h84s34h9g60s30c1g60o30c1g6so32ghp8h33egi568pk6h9g64o30c1g60o30c1g60o30c1g60o32c1g60o30c1g8h23cc2388ojida36t2jgg9k610k6ga48p2j6cho6grkcdhm84qg?xoauth_requestor_id=bobert@example.com"/>
284
+ <author>
285
+ <name>Bobert Jones</name>
286
+ <email>bobert@example.com</email>
287
+ </author>
288
+ <gd:comments>
289
+ <gd:feedLink href="https://www.google.com"/>
290
+ </gd:comments>
291
+ <gd:eventStatus value="http://schemas.google.com/g/2005#event.confirmed"/>
292
+ <gd:where valueString="Omicron Persei 8 "/>
293
+ <gd:who email="planet.express@example.com" rel="http://schemas.google.com/g/2005#event.attendee" valueString="Planet Express">
294
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.invited"/>
295
+ </gd:who>
296
+ <gd:who email="bobert@example.com" rel="http://schemas.google.com/g/2005#event.attendee" valueString="Bobert Jones">
297
+ <gd:attendeeStatus value="http://schemas.google.com/g/2005#event.accepted"/>
298
+ </gd:who>
299
+ <gd:who email="zapp@example.com" rel="http://schemas.google.com/g/2005#event.organizer" valueString="Zapp Brannigan"/>
300
+ <gd:when endTime="2013-04-03T16:00:00.000-07:00" startTime="2013-04-03T15:00:00.000-07:00"/>
301
+ <gd:transparency value="http://schemas.google.com/g/2005#event.opaque"/>
302
+ <gd:visibility value="http://schemas.google.com/g/2005#event.default"/>
303
+ <gCal:anyoneCanAddSelf value="false"/>
304
+ <gCal:guestsCanInviteOthers value="false"/>
305
+ <gCal:guestsCanModify value="false"/>
306
+ <gCal:guestsCanSeeGuests value="true"/>
307
+ <gCal:privateCopy value="true"/>
308
+ <gCal:sequence value="0"/>
309
+ <gCal:uid value="040000008200E00074C5B7101A82E00800000000701B9DF7BE23CE01000000000000000010000000DD60CB195C7E8A40ACADFE32847F66A5"/>
310
+ </entry>
311
+ <entry gd:etag="&quot;GE4CRgFGeip7JGA6WhJa&quot;" gd:kind="calendar#event">
312
+ <id>http://www.google.com/calendar/feeds/bobert%40example.com/events/qhehinrhaaqi7lsc1p8ukn1k1k</id>
313
+ <published>2013-03-07T23:19:14.000Z</published>
314
+ <updated>2013-03-07T23:19:34.000Z</updated>
315
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/g/2005#event"/>
316
+ <title>Alumni Event</title>
317
+ <content/>
318
+ <link rel="alternate" type="text/html" href="https://www.google.com/calendar/event?eid=cWhlaGlucmhhYXFpN2xzYzFwOHVrbjFrMWsga2FpQGRpc3RpbGwuY2M" title="alternate"/>
319
+ <link rel="self" type="application/atom+xml" href="https://www.google.com/calendar/feeds/bobert%40example.com/private/full/qhehinrhaaqi7lsc1p8ukn1k1k?xoauth_requestor_id=bobert@example.com"/>
320
+ <author>
321
+ <name>Bobert Jones</name>
322
+ <email>bobert@example.com</email>
323
+ </author>
324
+ <gd:comments>
325
+ <gd:feedLink href="https://www.google.com"/>
326
+ </gd:comments>
327
+ <gd:eventStatus value="http://schemas.google.com/g/2005#event.confirmed"/>
328
+ <gd:where valueString="Mars University"/>
329
+ <gd:who email="bobert@example.com" rel="http://schemas.google.com/g/2005#event.organizer" valueString="bobert@example.com"/>
330
+ <gd:when endTime="2013-04-04T19:00:00.000-07:00" startTime="2013-04-04T18:00:00.000-07:00"/>
331
+ <gd:transparency value="http://schemas.google.com/g/2005#event.opaque"/>
332
+ <gd:visibility value="http://schemas.google.com/g/2005#event.default"/>
333
+ <gCal:anyoneCanAddSelf value="false"/>
334
+ <gCal:guestsCanInviteOthers value="true"/>
335
+ <gCal:guestsCanModify value="false"/>
336
+ <gCal:guestsCanSeeGuests value="true"/>
337
+ <gCal:sequence value="0"/>
338
+ <gCal:uid value="qhehinrhaaqi7lsc1p8ukn1k1k@google.com"/>
339
+ </entry>
340
+ <entry gd:etag="&quot;EEsITwBGeSp7JGA6WhNT&quot;" gd:kind="calendar#event">
341
+ <id>http://www.google.com/calendar/feeds/bobert%40example.com/events/bqup9tohkrmo7685ks63jrkokc</id>
342
+ <published>2013-04-02T23:26:58.000Z</published>
343
+ <updated>2013-04-02T23:27:57.000Z</updated>
344
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/g/2005#event"/>
345
+ <title>The Donbot</title>
346
+ <content/>
347
+ <link rel="alternate" type="text/html" href="https://www.google.com/calendar/event?eid=YnF1cDl0b2hrcm1vNzY4NWtzNjNqcmtva2Mga2FpQGRpc3RpbGwuY2M" title="alternate"/>
348
+ <link rel="self" type="application/atom+xml" href="https://www.google.com/calendar/feeds/bobert%40example.com/private/full/bqup9tohkrmo7685ks63jrkokc?xoauth_requestor_id=bobert@example.com"/>
349
+ <author>
350
+ <name>Bobert Jones</name>
351
+ <email>bobert@example.com</email>
352
+ </author>
353
+ <gd:comments>
354
+ <gd:feedLink href="https://www.google.com"/>
355
+ </gd:comments>
356
+ <gd:eventStatus value="http://schemas.google.com/g/2005#event.confirmed"/>
357
+ <gd:where valueString="Elzar's"/>
358
+ <gd:who email="bobert@example.com" rel="http://schemas.google.com/g/2005#event.organizer" valueString="bobert@example.com"/>
359
+ <gd:when endTime="2013-04-05T11:00:00.000-07:00" startTime="2013-04-05T10:00:00.000-07:00"/>
360
+ <gd:transparency value="http://schemas.google.com/g/2005#event.opaque"/>
361
+ <gd:visibility value="http://schemas.google.com/g/2005#event.default"/>
362
+ <gCal:anyoneCanAddSelf value="false"/>
363
+ <gCal:guestsCanInviteOthers value="true"/>
364
+ <gCal:guestsCanModify value="false"/>
365
+ <gCal:guestsCanSeeGuests value="true"/>
366
+ <gCal:sequence value="0"/>
367
+ <gCal:uid value="bqup9tohkrmo7685ks63jrkokc@google.com"/>
368
+ </entry>
369
+ </feed>
@@ -0,0 +1,39 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:apps="http://schemas.google.com/apps/2006" xmlns:gd="http://schemas.google.com/g/2005" gd:etag="W/&quot;YDwqeyI.&quot;">
3
+ <id>https://apps-apis.google.com/a/feeds/example.com/user/2.0</id>
4
+ <updated>1970-01-01T00:00:00.000Z</updated>
5
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/apps/2006#user"/>
6
+ <title>Users</title>
7
+ <link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="https://apps-apis.google.com/a/feeds/example.com/user/2.0"/>
8
+ <link rel="http://schemas.google.com/g/2005#post" type="application/atom+xml" href="https://apps-apis.google.com/a/feeds/example.com/user/2.0"/>
9
+ <link rel="self" type="application/atom+xml" href="https://apps-apis.google.com/a/feeds/example.com/user/2.0"/>
10
+ <openSearch:startIndex>1</openSearch:startIndex>
11
+ <entry gd:etag="W/&quot;YDwqeyI.&quot;">
12
+ <id>https://apps-apis.google.com/a/feeds/example.com/user/2.0/fry</id>
13
+ <updated>1970-01-01T00:00:00.000Z</updated>
14
+ <app:edited xmlns:app="http://www.w3.org/2007/app">1970-01-01T00:00:00.000Z</app:edited>
15
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/apps/2006#user"/>
16
+ <title>fry</title>
17
+ <link rel="self" type="application/atom+xml" href="https://apps-apis.google.com/a/feeds/example.com/user/2.0/fry"/>
18
+ <link rel="edit" type="application/atom+xml" href="https://apps-apis.google.com/a/feeds/example.com/user/2.0/fry"/>
19
+ <apps:login userName="fry" suspended="false" ipWhitelisted="false" admin="false" changePasswordAtNextLogin="false" agreedToTerms="true"/>
20
+ <apps:quota limit="25600"/>
21
+ <apps:name familyName="Fry" givenName="Philip"/>
22
+ <gd:feedLink rel="http://schemas.google.com/apps/2006#user.nicknames" href="https://apps-apis.google.com/a/feeds/example.com/nickname/2.0?username=fry"/>
23
+ <gd:feedLink rel="http://schemas.google.com/apps/2006#user.emailLists" href="https://apps-apis.google.com/a/feeds/example.com/emailList/2.0?recipient=fry%40example.com"/>
24
+ </entry>
25
+ <entry gd:etag="W/&quot;YDwqeyI.&quot;">
26
+ <id>https://apps-apis.google.com/a/feeds/example.com/user/2.0/bender</id>
27
+ <updated>1970-01-01T00:00:00.000Z</updated>
28
+ <app:edited xmlns:app="http://www.w3.org/2007/app">1970-01-01T00:00:00.000Z</app:edited>
29
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/apps/2006#user"/>
30
+ <title>bender</title>
31
+ <link rel="self" type="application/atom+xml" href="https://apps-apis.google.com/a/feeds/example.com/user/2.0/bender"/>
32
+ <link rel="edit" type="application/atom+xml" href="https://apps-apis.google.com/a/feeds/example.com/user/2.0/bender"/>
33
+ <apps:login userName="bender" suspended="true" ipWhitelisted="false" admin="true" changePasswordAtNextLogin="false" agreedToTerms="true"/>
34
+ <apps:quota limit="25600"/>
35
+ <apps:name familyName="Rodriguez" givenName="Bender"/>
36
+ <gd:feedLink rel="http://schemas.google.com/apps/2006#user.nicknames" href="https://apps-apis.google.com/a/feeds/example.com/nickname/2.0?username=bender"/>
37
+ <gd:feedLink rel="http://schemas.google.com/apps/2006#user.emailLists" href="https://apps-apis.google.com/a/feeds/example.com/emailList/2.0?recipient=bender%40example.com"/>
38
+ </entry>
39
+ </feed>
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+ include Gaah::Calendar
3
+
4
+ describe Event do
5
+ let(:xml) { fixture('calendar.xml') }
6
+ let(:events) { Nokogiri::XML(xml)/:entry }
7
+ let(:event) { Event.new(events.first) }
8
+
9
+ describe '#initialize' do
10
+ it 'parses ID' do
11
+ event.id.should == 'http://www.google.com/calendar/feeds/bobert%40example.com/events/n9ommrehhjm5dc6q89ofkm3f8g'
12
+ end
13
+
14
+ it 'parses published time' do
15
+ event.published.should == Time.parse('2013-03-25T16:49:48.000Z')
16
+ end
17
+
18
+ it 'parses updated time' do
19
+ event.updated.should == Time.parse('2013-03-25T17:03:10.000Z')
20
+ end
21
+
22
+ it 'parses title' do
23
+ event.title.should == 'MomCorp'
24
+ end
25
+
26
+ it 'parses content' do
27
+ event.content.should == ''
28
+ end
29
+
30
+ it 'parses status' do
31
+ event.status.should == 'confirmed'
32
+ end
33
+
34
+ describe :who do
35
+ let(:who) { event.who }
36
+
37
+ it 'parses who' do
38
+ who.length.should == 2
39
+ who.first.should be_an_instance_of(Gaah::Calendar::Who)
40
+ end
41
+
42
+ it 'parses name' do
43
+ who.first.name.should == 'Bobert Jones'
44
+ end
45
+
46
+ it 'parses email' do
47
+ who.first.email.should == 'bobert@example.com'
48
+ end
49
+ end
50
+
51
+ describe :when do
52
+ let(:_when) { event.when }
53
+
54
+ it 'parses when' do
55
+ _when.should be_an_instance_of(When)
56
+ end
57
+
58
+ it 'parses start_time' do
59
+ _when.start_time.should == Time.parse('2013-04-01 10:00:00 -0700')
60
+ end
61
+
62
+ it 'parses end_time' do
63
+ _when.end_time.should == Time.parse('2013-04-01 11:00:00 -0700')
64
+ end
65
+ end
66
+
67
+ it 'parses where' do
68
+ event.where.should == ''
69
+ end
70
+
71
+ describe :author do
72
+ let(:author) { event.author }
73
+
74
+ it 'parses email' do
75
+ author.email.should == "bobert@example.com"
76
+ end
77
+
78
+ it 'parses name' do
79
+ author.name.should == "Bobert Jones"
80
+ end
81
+ end
82
+
83
+ it 'parses transparency' do
84
+ event.transparency.should == 'opaque'
85
+ end
86
+
87
+ it 'parses visibility' do
88
+ event.visibility.should == 'default'
89
+ end
90
+ end
91
+
92
+ describe '.batch_create' do
93
+ let(:processed_events) { Event.batch_create(events) }
94
+
95
+ it 'parses events' do
96
+ processed_events.count.should == 10
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ include Gaah::Provisioning
3
+
4
+ describe User do
5
+ let(:xml) { fixture('provisioning.xml') }
6
+ let(:users) { Nokogiri::XML(xml)/:entry }
7
+ let(:user) { User.new(users.first) }
8
+
9
+ describe '#initialize' do
10
+ it 'parses ID' do
11
+ user.id.should == 'https://apps-apis.google.com/a/feeds/example.com/user/2.0/fry'
12
+ end
13
+
14
+ it 'parses suspended' do
15
+ user.suspended.should == false
16
+ end
17
+
18
+ it 'parses admin' do
19
+ user.admin.should == false
20
+ end
21
+
22
+ it 'parses title' do
23
+ user.title.should == 'fry'
24
+ end
25
+
26
+ it 'parses user_name' do
27
+ user.user_name.should == 'fry'
28
+ end
29
+
30
+ it 'parses name' do
31
+ user.name.should == 'Philip Fry'
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,8 @@
1
+ require 'gaah'
2
+ include Gaah
3
+
4
+ SPEC_ROOT = File.dirname(__FILE__)
5
+
6
+ def fixture(name)
7
+ File.open(File.join(SPEC_ROOT, '/fixtures/', name)).read
8
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gaah
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Hwan-Joon Choi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-04-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: oauth
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '0.4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '0.4'
41
+ - !ruby/object:Gem::Dependency
42
+ name: queryparams
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 0.0.3
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.0.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '2.5'
69
+ description: Google Apps Marketplace API wrapper in ruby for Distill.
70
+ email:
71
+ - hc5duke@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - .rspec
78
+ - Gemfile
79
+ - Gemfile.lock
80
+ - README.md
81
+ - gaah.gemspec
82
+ - lib/gaah.rb
83
+ - lib/gaah/api_client.rb
84
+ - lib/gaah/api_model.rb
85
+ - lib/gaah/base.rb
86
+ - lib/gaah/calendar/api.rb
87
+ - lib/gaah/calendar/event.rb
88
+ - lib/gaah/calendar/when.rb
89
+ - lib/gaah/calendar/who.rb
90
+ - lib/gaah/provisioning/api.rb
91
+ - lib/gaah/provisioning/user.rb
92
+ - lib/gaah/version.rb
93
+ - spec/fixtures/calendar.xml
94
+ - spec/fixtures/provisioning.xml
95
+ - spec/models/event_spec.rb
96
+ - spec/models/user_spec.rb
97
+ - spec/spec_helper.rb
98
+ homepage: https://github.com/distill-inc/gaah
99
+ licenses: []
100
+ metadata: {}
101
+ post_install_message:
102
+ rdoc_options: []
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubyforge_project:
117
+ rubygems_version: 2.0.3
118
+ signing_key:
119
+ specification_version: 4
120
+ summary: Limited API Wrapper for Google Apps API.
121
+ test_files:
122
+ - spec/fixtures/calendar.xml
123
+ - spec/fixtures/provisioning.xml
124
+ - spec/models/event_spec.rb
125
+ - spec/models/user_spec.rb
126
+ - spec/spec_helper.rb