google_apps 0.5 → 0.9

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.
@@ -0,0 +1,20 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = 'google_apps'
3
+ spec.version = '0.9'
4
+ spec.date = '2013-03-11'
5
+ spec.license = "MIT"
6
+ spec.summary = 'Google Apps APIs'
7
+ spec.description = 'Library for interfacing with Google Apps\' Domain and Application APIs'
8
+ spec.authors = ['Glen Holcomb', 'Will Read']
9
+ spec.files = Dir.glob(File.join('**', 'lib', '**', '*.rb'))
10
+ spec.homepage = 'https://github.com/LeakyBucket/google_apps'
11
+
12
+ spec.add_dependency('libxml-ruby', '>= 2.2.2')
13
+ spec.add_dependency('httparty')
14
+
15
+ spec.add_development_dependency 'rspec'
16
+ spec.add_development_dependency 'webmock'
17
+
18
+ spec.files = `git ls-files`.split("\n")
19
+ spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ end
@@ -29,16 +29,13 @@ module GoogleApps
29
29
 
30
30
  ENTRY_TAG = ["<atom:entry xmlns:atom=\"#{NAMESPACES[:atom]}\" xmlns:apps=\"#{NAMESPACES[:apps]}\" xmlns:gd=\"#{NAMESPACES[:gd]}\">", '</atom:entry>']
31
31
 
32
-
33
-
34
- #
35
- # Adds a Module Function that creates a corresponding document.
32
+ # Adds a Module Function that creates a corresponding document.
36
33
  # This allows for a centralized location for document creation.
37
- #
38
- # @param [String] type
39
- #
40
- # @visibility public
41
- # @return
34
+ #
35
+ # @param [String] type should correspond to the class name
36
+ #
37
+ # @visibility public
38
+ # @return
42
39
  def add_doc_dispatcher(type)
43
40
  eval "def #{type}(*args)\n #{type.camel_up}.new *args\nend" # Needs __file__ and __line__
44
41
  module_function type.to_sym
@@ -13,8 +13,8 @@ module GoogleApps
13
13
  def to_s
14
14
  @doc.to_s
15
15
  end
16
-
17
- # start_date specifies a start date for the extract.
16
+
17
+ # start_date specifies a start date for the extract.
18
18
  # Matching results that occurred before this date will
19
19
  # not be included in the result set. start_date takes
20
20
  # a string as an argument, the format is as follows:
@@ -31,7 +31,7 @@ module GoogleApps
31
31
  add_prop('beginDate', date)
32
32
  end
33
33
 
34
- # end_date specifies an end date for the extract.
34
+ # end_date specifies an end date for the extract.
35
35
  # Matching results that occurred past this date will
36
36
  # not be included in the result set. end_date takes
37
37
  # a string as an argument, the format is as follows:
@@ -49,7 +49,7 @@ module GoogleApps
49
49
  end
50
50
 
51
51
  # include_deleted will specify that matches which
52
- # have been deleted should be returned as well.
52
+ # have been deleted should be returned as well.
53
53
  # The default is to omit deleted matches.
54
54
  #
55
55
  # include_deleted
@@ -72,7 +72,7 @@ module GoogleApps
72
72
  end
73
73
 
74
74
  # content specifies the data to be returned in the
75
- # mailbox export. There are two valid arguments:
75
+ # mailbox export. There are two valid arguments:
76
76
  # 'FULL_MESSAGE' or 'HEADER_ONLY'
77
77
  #
78
78
  # content 'HEADER_ONLY'
@@ -82,7 +82,6 @@ module GoogleApps
82
82
  add_prop('packageContent', type)
83
83
  end
84
84
 
85
-
86
85
  private
87
86
 
88
87
  # set_header adds the appropriate XML boilerplate for
@@ -4,16 +4,12 @@ module GoogleApps
4
4
  # TODO: Google's feed responses are inconsistent. Will need special fun time, assholes.
5
5
  attr_reader :doc, :items, :next_page
6
6
 
7
- # TODO: Figure out how to handle Group Members. The regex below
8
- # doesn't work in that case as group members also have group in
9
- # the id url.
7
+ # FIXED: If we grab the first id element in the feed we can simply scan it.
10
8
  TYPE_MATCH = /\/(user|nickname|group|member)/
11
9
 
12
10
 
13
11
  def initialize(xml)
14
- id_element = xml.scan(/<id.*?\/id/).first
15
- matches = id_element.scan(TYPE_MATCH).flatten
16
- type = matches.join '_'
12
+ type = determine_type xml # This only works when xml is a string.
17
13
 
18
14
  super(xml)
19
15
 
@@ -109,6 +105,21 @@ module GoogleApps
109
105
  def entry_wrap(content_array)
110
106
  content_array.unshift(Atom::ENTRY_TAG[0]).push(Atom::ENTRY_TAG[1])
111
107
  end
108
+
109
+
110
+ #
111
+ # Determine the feed type from the feed id element.
112
+ #
113
+ # @param [String] xml
114
+ #
115
+ # @visibility public
116
+ # @return snake cased doc type
117
+ def determine_type(xml)
118
+ id_element = xml.scan(/<id.*?\/id/).first
119
+ matches = id_element.scan(TYPE_MATCH).flatten
120
+
121
+ matches.join '_'
122
+ end
112
123
  end
113
124
  end
114
125
  end
@@ -1,71 +1,39 @@
1
1
  module GoogleApps
2
2
  class DocumentHandler
3
- # TODO: Should get type list from Document Parent class, not format namespace.
4
- attr_accessor :format
5
-
6
- def initialize(args)
7
- set_format args[:format]
3
+ def initialize
4
+ @documents = look_up_doc_types
8
5
  end
9
6
 
10
-
11
7
  # create_doc creates a document of the specified format
12
8
  # from the given string.
13
9
  def create_doc(text, type = nil)
14
10
  @documents.include?(type) ? doc_of_type(text, type) : unknown_type(text)
15
11
  end
16
12
 
17
-
18
13
  # unknown_type takes a string and returns a document of
19
14
  # of the corresponding @format.
20
15
  def unknown_type(text)
21
- case @format
22
- when :atom, :xml
23
- Atom::XML::Document.string(text)
24
- end
16
+ Atom::XML::Document.string(text)
25
17
  end
26
18
 
27
-
28
- # format= sets the format for the DocumentHandler
29
- def format=(format)
30
- set_format format
31
- end
32
-
33
-
34
19
  # doc_of_type takes a document type and a string and
35
20
  # returns a document of that type in the current format.
36
21
  def doc_of_type(text, type)
37
- raise "No #{@format.to_s.capitalize} document of type: #{type}" unless @documents.include?(type.to_s)
22
+ raise "No Atom document of type: #{type}" unless @documents.include?(type.to_s)
38
23
 
39
- case @format
40
- when :atom, :xml
41
- GoogleApps::Atom.send(type, text)
42
- end
24
+ GoogleApps::Atom.send(type, text)
43
25
  end
44
26
 
45
-
46
-
47
27
  private
48
28
 
49
-
50
29
  # look_up_doc_types returns a list of document types the
51
30
  # library supports in the current format.
52
31
  def look_up_doc_types
53
- case @format
54
- when :atom, :xml
55
- Atom::Document.types.inject([]) { |types, subclass| types | [sub_to_meth(subclass)] }
56
- end
32
+ Atom::Document.types.map { |subclass| sub_to_meth(subclass) }
57
33
  end
58
34
 
59
-
60
35
  def sub_to_meth(subclass) # TODO: This shouldn't be both here and in GoogleApps::Atom::Document
61
36
  subclass.to_s.split('::').last.scan(/[A-Z][a-z0-9]+/).map(&:downcase).join('_')
62
37
  end
63
-
64
-
65
- # set_format Sets @format and @documents
66
- def set_format(format)
67
- @format = format
68
- @documents = look_up_doc_types
69
- end
70
38
  end
71
39
  end
@@ -4,53 +4,35 @@ require 'rexml/document'
4
4
 
5
5
  module GoogleApps
6
6
  class Transport
7
- attr_reader :request, :response, :domain, :feeds
8
- attr_accessor :auth, :user, :group, :nickname, :export, :group, :requester, :migration
7
+ attr_reader :domain, :token
8
+ attr_reader :user, :group, :nickname, :export, :pubkey, :requester, :migration
9
9
 
10
- SUCCESS_CODES = [200, 201, 202]
11
10
  BOUNDARY = "=AaB03xDFHT8xgg"
12
11
  PAGE_SIZE = {
13
12
  user: 100,
14
13
  group: 200
15
14
  }
15
+ FEEDS_ROOT = 'https://apps-apis.google.com/a/feeds'
16
16
 
17
- def initialize(domain, targets = {})
18
- @feeds_root = 'https://apps-apis.google.com/a/feeds'
19
- @auth = targets[:auth] || "https://www.google.com/accounts/ClientLogin"
20
- @user = targets[:user] || "#{@feeds_root}/#{domain}/user/2.0"
21
- @pubkey = targets[:pubkey] || "#{@feeds_root}/compliance/audit/publickey/#{domain}"
22
- @migration = targets[:migration] || "#{@feeds_root}/migration/2.0/#{domain}"
23
- @group = targets[:group] || "#{@feeds_root}/group/2.0/#{domain}"
24
- @nickname = targets[:nickname] || "#{@feeds_root}/#{domain}/nickname/2.0"
25
- @audit = "#{@feeds_root}/compliance/audit/mail"
26
- @export = targets[:export] || "#{@audit}/export/#{domain}"
27
- @monitor = targets[:monitor] || "#{@audit}/monitor/#{domain}"
28
- @domain = domain
29
- @requester = AppsRequest || targets[:requester]
30
- @doc_handler = DocumentHandler.new format: (targets[:format] || :atom)
31
- @token = nil
32
- @response = nil
33
- @request = nil
34
- @feeds = []
35
- end
36
-
37
-
38
- # authenticate will take the provided account and
39
- # password and attempt to authenticate them with
40
- # Google
41
- #
42
- # authenticate 'username@domain', 'password'
43
- #
44
- # authenticate returns the HTTP response received
45
- # from Google
46
- def authenticate(account, pass)
47
- add(@auth, nil, auth_body(account, pass), :auth)
17
+ def initialize(options)
18
+ @domain = options[:domain]
19
+ @token = options[:token]
20
+ @refresh_token = options[:refresh_token]
21
+ @token_changed_callback = options[:token_changed_callback]
48
22
 
49
- set_auth_token
23
+ @user = "#{FEEDS_ROOT}/#{@domain}/user/2.0"
24
+ @pubkey = "#{FEEDS_ROOT}/compliance/audit/publickey/#{@domain}"
25
+ @migration = "#{FEEDS_ROOT}/migration/2.0/#{@domain}"
26
+ @group = "#{FEEDS_ROOT}/group/2.0/#{@domain}"
27
+ @nickname = "#{FEEDS_ROOT}/#{@domain}/nickname/2.0"
50
28
 
51
- @response
52
- end
29
+ audit_root = "#{FEEDS_ROOT}/compliance/audit/mail"
30
+ @export = "#{audit_root}/export/#{@domain}"
31
+ @monitor = "#{audit_root}/monitor/#{@domain}"
53
32
 
33
+ @requester = AppsRequest
34
+ @doc_handler = DocumentHandler.new
35
+ end
54
36
 
55
37
  # request_export performs the GoogleApps API call to
56
38
  # generate a mailbox export. It takes the username
@@ -62,9 +44,11 @@ module GoogleApps
62
44
  # request_export returns the request ID on success or
63
45
  # the HTTP response object on failure.
64
46
  def request_export(username, document)
65
- result = add(@export + "/#{username}", :export_response, document)
47
+ response = add(export + "/#{username}", document)
48
+ process_response(response)
49
+ export = create_doc(response.body, :export_response)
66
50
 
67
- result.find('//apps:property').inject(nil) do |request_id, node|
51
+ export.find('//apps:property').inject(nil) do |request_id, node|
68
52
  node.attributes['name'] == 'requestId' ? node.attributes['value'].to_i : request_id
69
53
  end
70
54
  end
@@ -79,26 +63,28 @@ module GoogleApps
79
63
  # export_status will return the body of the HTTP response
80
64
  # from Google
81
65
  def export_status(username, req_id)
82
- get(@export + "/#{username}", :export_status, req_id)
66
+ response = get(export + "/#{username}", req_id)
67
+ process_response(response)
68
+ create_doc(response.body, :export_status)
83
69
  end
84
70
 
71
+ def create_doc(response_body, type = nil)
72
+ @doc_handler.create_doc(response_body, type)
73
+ end
85
74
 
86
75
  # export_ready? checks the export_status response for the
87
76
  # presence of an apps:property element with a fileUrl name
88
77
  # attribute.
89
78
  #
90
- # export_ready? 'lholcomb2', 82834
79
+ # export_ready?(export_status('username', 847576))
91
80
  #
92
81
  # export_ready? returns true if there is a fileUrl present
93
82
  # in the response and false if there is no fileUrl present
94
83
  # in the response.
95
- def export_ready?(username, req_id)
96
- export_status(username, req_id)
97
-
98
- !(export_file_urls.empty?)
84
+ def export_ready?(export_status_doc)
85
+ export_file_urls(export_status_doc).any?
99
86
  end
100
87
 
101
-
102
88
  # fetch_export downloads the mailbox export from Google.
103
89
  # It takes a username, request id and a filename as
104
90
  # arguments. If the export consists of more than one file
@@ -110,8 +96,9 @@ module GoogleApps
110
96
  # fetch_export reutrns nil in the event that the export is
111
97
  # not yet ready.
112
98
  def fetch_export(username, req_id, filename)
113
- if export_ready?(username, req_id)
114
- download_export(filename).each_with_index { |url, index| url.gsub!(/.*/, "#{filename}#{index}")}
99
+ export_status_doc = export_status(username, req_id)
100
+ if export_ready?(export_status_doc)
101
+ download_export(export_status_doc, filename).each_with_index { |url, index| url.gsub!(/.*/, "#{filename}#{index}")}
115
102
  else
116
103
  nil
117
104
  end
@@ -123,10 +110,10 @@ module GoogleApps
123
110
  #
124
111
  # download 'url', 'save_file'
125
112
  def download(url, filename)
126
- @request = @requester.new :get, URI(url), headers(:other)
113
+ request = requester.new :get, URI(url), headers(:other)
127
114
 
128
115
  File.open(filename, "w") do |file|
129
- file.puts @request.send_request.body
116
+ file.puts request.send_request.body
130
117
  end
131
118
  end
132
119
 
@@ -139,13 +126,11 @@ module GoogleApps
139
126
  # get 'endpoint', 'username'
140
127
  #
141
128
  # get returns the HTTP response received from Google.
142
- def get(endpoint, type, id = nil)
129
+ def get(endpoint, id = nil)
143
130
  id ? uri = URI(endpoint + build_id(id)) : uri = URI(endpoint)
144
- @request = @requester.new :get, uri, headers(:other)
145
-
146
- @response = @request.send_request
131
+ request = requester.new :get, uri, headers(:other)
147
132
 
148
- process_response(type)
133
+ request.send_request
149
134
  end
150
135
 
151
136
 
@@ -158,9 +143,14 @@ module GoogleApps
158
143
  #
159
144
  # get_users returns the final response from google.
160
145
  def get_users(options = {})
161
- get_all :users, options
162
- end
146
+ limit = options[:limit] || 1000000
147
+ response = get(user + "?startUsername=#{options[:start]}")
148
+ process_response(response)
149
+
150
+ pages = fetch_pages(response, limit, :feed)
163
151
 
152
+ return_all(pages)
153
+ end
164
154
 
165
155
  # get_groups retrieves all the groups from the domain
166
156
  #
@@ -168,31 +158,14 @@ module GoogleApps
168
158
  #
169
159
  # get_groups returns the final response from Google.
170
160
  def get_groups(options = {})
171
- get_all :groups, options
172
- end
161
+ limit = options[:limit] || 1000000
162
+ response = get(group + "#{options[:extra]}" + "?startGroup=#{options[:start]}")
163
+ process_response(response, :feed)
164
+ pages = fetch_pages(response, limit, :feed)
173
165
 
174
-
175
- # get_all retrieves a batch of records of the specified type
176
- # from google. You must specify the type of object you want
177
- # to retreive. You can also specify a start point and a limit.
178
- #
179
- # get_all 'users', start: 'lholcomb2', limit: 300
180
- #
181
- # get_all returns the HTTP response received from Google.
182
- def get_all(type, options = {})
183
- @feeds, page = [], 0
184
- type = normalize_type type
185
-
186
- options[:limit] ? limit = options[:limit] : limit = 1000000
187
- options[:start] ? get(instance_variable_get("@#{type}") + "#{options[:extra]}" + "?#{start_query(type)}=#{options[:start]}", :feed) : get(instance_variable_get("@#{type}") + "#{options[:extra]}", :feed)
188
-
189
- fetch_feed(page, limit, :feed)
190
-
191
- #@response
192
- return_all
166
+ return_all(pages)
193
167
  end
194
168
 
195
-
196
169
  # Retrieves the members of the requested group.
197
170
  #
198
171
  # @param [String] group_id the Group ID in the Google Apps Environment
@@ -201,7 +174,7 @@ module GoogleApps
201
174
  # @return
202
175
  def get_members_of(group_id, options = {})
203
176
  options[:extra] = "/#{group_id}/member"
204
- get_all :groups, options
177
+ get_groups options
205
178
  end
206
179
 
207
180
 
@@ -216,7 +189,9 @@ module GoogleApps
216
189
  #
217
190
  # add_member_to returns the response received from Google.
218
191
  def add_member_to(group_id, document)
219
- add(@group + "/#{group_id}/member", nil, document)
192
+ response = add(group + "/#{group_id}/member", document)
193
+ process_response(response)
194
+ create_doc(response.body)
220
195
  end
221
196
 
222
197
 
@@ -227,7 +202,7 @@ module GoogleApps
227
202
  # @visibility public
228
203
  # @return
229
204
  def add_owner_to(group_id, document)
230
- add(@group + "/#{group_id}/owner", nil, document)
205
+ add(group + "/#{group_id}/owner", nil, document)
231
206
  end
232
207
 
233
208
  # TODO: Refactor delete froms.
@@ -239,18 +214,16 @@ module GoogleApps
239
214
  #
240
215
  # delete_member_from returns the respnse received from Google.
241
216
  def delete_member_from(group_id, member_id)
242
- delete(@group + "/#{group_id}/member", member_id)
217
+ delete(group + "/#{group_id}/member", member_id)
243
218
  end
244
219
 
245
-
246
- #
247
220
  # @param [String] group_id Email address of group
248
221
  # @param [String] owner_id Email address of owner to remove
249
222
  #
250
223
  # @visibility public
251
224
  # @return
252
225
  def delete_owner_from(group_id, owner_id)
253
- delete(@group + "/#{group_id}/owner", owner_id)
226
+ delete(group + "/#{group_id}/owner", owner_id)
254
227
  end
255
228
 
256
229
 
@@ -273,15 +246,13 @@ module GoogleApps
273
246
  # add 'endpoint', document
274
247
  #
275
248
  # add returns the HTTP response received from Google.
276
- def add(endpoint, type, document, header_type = nil)
249
+ def add(endpoint, document, header_type = nil)
277
250
  header_type = :others unless header_type
278
251
  uri = URI(endpoint)
279
- @request = @requester.new :post, uri, headers(header_type)
280
- @request.add_body document.to_s
281
-
282
- @response = @request.send_request
252
+ request = requester.new :post, uri, headers(header_type)
253
+ request.add_body document.to_s
283
254
 
284
- process_response type
255
+ request.send_request
285
256
  end
286
257
 
287
258
  # update is a generic target for method_missing. It is
@@ -290,17 +261,15 @@ module GoogleApps
290
261
  # It takes an API endpoint and a GoogleApps::Atom document
291
262
  # as arguments.
292
263
  #
293
- # update 'endpoint', document
264
+ # update 'endpoint', target, document
294
265
  #
295
266
  # update returns the HTTP response received from Google
296
- def update(endpoint, type, target, document)
267
+ def update(endpoint, target, document)
297
268
  uri = URI(endpoint + "/#{target}")
298
- @request = @requester.new :put, uri, headers(:other)
299
- @request.add_body document.to_s
269
+ request = requester.new :put, uri, headers(:other)
270
+ request.add_body document.to_s
300
271
 
301
- @response = @request.send_request
302
-
303
- process_response type
272
+ request.send_request
304
273
  end
305
274
 
306
275
  # delete is a generic target for method_missing. It is
@@ -313,9 +282,9 @@ module GoogleApps
313
282
  # delete returns the HTTP response received from Google.
314
283
  def delete(endpoint, id)
315
284
  uri = URI(endpoint + "/#{id}")
316
- @request = @requester.new :delete, uri, headers(:other)
285
+ request = requester.new :delete, uri, headers(:other)
317
286
 
318
- @response = @request.send_request
287
+ request.send_request
319
288
  end
320
289
 
321
290
  # migration performs mail migration from a local
@@ -327,47 +296,39 @@ module GoogleApps
327
296
  #
328
297
  # migrate returns the HTTP response received from Google.
329
298
  def migrate(username, properties, message)
330
- @request = @requester.new(:post, URI(@migration + "/#{username}/mail"), headers(:migration))
331
- @request.add_body multi_part(properties.to_s, message)
299
+ request = requester.new(:post, URI(migration + "/#{username}/mail"), headers(:migration))
300
+ request.add_body multi_part(properties.to_s, message)
332
301
 
333
- @request.send_request
302
+ request.send_request
334
303
  end
335
304
 
336
-
337
-
338
305
  def method_missing(name, *args)
339
306
  super unless name.match /([a-z]*)_([a-z]*)/
340
307
 
341
308
  case $1
342
309
  when "new", "add"
343
- self.send(:add, instance_variable_get("@#{$2}"), $2, *args)
310
+ response = self.send(:add, send($2), *args)
311
+ process_response(response)
312
+ create_doc(response.body, $2)
344
313
  when "delete"
345
- self.send(:delete, instance_variable_get("@#{$2}"), *args)
314
+ response = self.send(:delete, send($2), *args)
315
+ process_response(response)
316
+ create_doc(response.body, $2)
346
317
  when "update"
347
- self.send(:update, instance_variable_get("@#{$2}"), $2, *args)
318
+ response = self.send(:update, send($2), *args)
319
+ process_response(response)
320
+ create_doc(response.body, $2)
348
321
  when "get"
349
- self.send(:get, instance_variable_get("@#{$2}"), $2, *args)
322
+ response = self.send(:get, send($2), *args)
323
+ process_response(response)
324
+ create_doc(response.body, $2)
350
325
  else
351
326
  super
352
327
  end
353
328
  end
354
329
 
355
-
356
330
  private
357
331
 
358
-
359
- # auth_body generates the body for the authentication
360
- # request made by authenticate.
361
- #
362
- # auth_body 'username@domain', 'password'
363
- #
364
- # auth_body returns a string in the form of HTTP
365
- # query parameters.
366
- def auth_body(account, pass)
367
- "&Email=#{CGI::escape(account)}&Passwd=#{CGI::escape(pass)}&accountType=HOSTED&service=apps"
368
- end
369
-
370
-
371
332
  # build_id checks the id string. If it is formatted
372
333
  # as a query string it is returned as is. If not
373
334
  # a / is prepended to the id string.
@@ -375,19 +336,16 @@ module GoogleApps
375
336
  id =~ /^\?/ ? id : "/#{id}"
376
337
  end
377
338
 
378
-
379
- # export_file_urls searches @response for any apps:property elements with a
339
+ # export_file_urls searches an export status doc for any apps:property elements with a
380
340
  # fileUrl name attribute and returns an array of the values.
381
- def export_file_urls
382
- Atom::XML::Document.string(@response.body).find('//apps:property').inject([]) do |urls, prop|
383
- urls << prop.attributes['value'] if prop.attributes['name'].match 'fileUrl'
384
- urls
341
+ def export_file_urls(export_status_doc)
342
+ export_status_doc.find("//apps:property[contains(@name, 'fileUrl')]").collect do |prop|
343
+ prop.attributes['value']
385
344
  end
386
345
  end
387
346
 
388
-
389
- def download_export(filename)
390
- export_file_urls.each_with_index do |url, index|
347
+ def download_export(export, filename)
348
+ export_file_urls(export).each_with_index do |url, index|
391
349
  download(url, filename + "#{index}")
392
350
  end
393
351
  end
@@ -396,100 +354,58 @@ module GoogleApps
396
354
  # process_response takes the HTTPResponse and either returns a
397
355
  # document of the specified type or in the event of an error it
398
356
  # returns the HTTPResponse.
399
- def process_response(doc_type = nil)
400
- case doc_type
401
- when nil
402
- success_response? ? true : raise("Error: #{response.code}, #{response.message}")
403
- else
404
- success_response? ? @doc_handler.create_doc(@response.body, doc_type) : raise("Error: #{response.code}, #{response.message}")
405
- end
357
+ def process_response(response)
358
+ raise("Error: #{response.code}, #{response.message}") unless success_response?(response)
406
359
  end
407
360
 
408
-
409
- # error_response? checks to see if Google Responded with a success
410
- # code.
411
- def success_response?
412
- SUCCESS_CODES.include?(@response.code.to_i)
361
+ def success_response?(response)
362
+ response.kind_of?(Net::HTTPSuccess)
413
363
  end
414
364
 
415
-
416
- #
417
-
418
365
  # Takes all the items in each feed and puts them into one array.
419
366
  #
420
367
  # @visibility private
421
368
  # @return Array of Documents
422
- def return_all
423
- @feeds.inject([]) do |results, feed|
369
+ def return_all(pages)
370
+ pages.inject([]) do |results, feed|
424
371
  results | feed.items
425
372
  end
426
373
  end
427
374
 
428
-
429
- # Grab the auth token from the response body
430
- def set_auth_token
431
- @response.body.split("\n").grep(/auth=(.*)/i)
432
-
433
- @token = $1
434
- end
435
-
436
-
437
375
  # get_next_page retrieves the next page in the response.
438
- def get_next_page(type)
439
- get @feeds.last.next_page, type
440
- add_feed
376
+ def get_next_page(next_page_url, type)
377
+ response = get(next_page_url)
378
+ process_response(response)
379
+ GoogleApps::Atom.feed(response.body)
441
380
  end
442
381
 
443
382
 
444
383
  # fetch_feed retrieves the remaining pages in the request.
445
384
  # It takes a page and a limit as arguments.
446
- def fetch_feed(page, limit, type)
447
- add_feed
448
- page += 1
385
+ def fetch_pages(response, limit, type)
386
+ pages = [GoogleApps::Atom.feed(response.body)]
449
387
 
450
- while (@feeds.last.next_page) and (page * PAGE_SIZE[:user] < limit)
451
- get_next_page type
452
- page += 1
388
+ while (pages.last.next_page) and (pages.count * PAGE_SIZE[:user] < limit)
389
+ pages << get_next_page(pages.last.next_page, type)
453
390
  end
391
+ pages
454
392
  end
455
393
 
456
-
457
- # start_query builds the value for the starting point
458
- # query string used for retrieving batches of objects
459
- # from Google.
460
- def start_query(type)
461
- case type
462
- when 'user'
463
- "startUsername"
464
- when 'group'
465
- "startGroup"
466
- end
394
+ def singularize(type)
395
+ type.to_s.gsub(/s$/, '')
467
396
  end
468
397
 
469
-
470
- def normalize_type(type)
471
- type.to_s.gsub!(/\w*s$/) { |match| match[0..-2] }
472
- end
473
-
474
-
475
- # add_feed adds a feed to the @feeds array.
476
- def add_feed
477
- @feeds << GoogleApps::Atom.feed(@response.body)
478
- end
479
-
480
-
481
398
  def headers(category)
482
399
  case category
483
400
  when :auth
484
401
  [['content-type', 'application/x-www-form-urlencoded']]
485
402
  when :migration
486
- [['content-type', "multipart/related; boundary=\"#{BOUNDARY}\""], ['authorization', "GoogleLogin auth=#{@token}"]]
403
+ [['content-type', "multipart/related; boundary=\"#{BOUNDARY}\""], ['Authorization', "OAuth #{@token}"]]
487
404
  else
488
- [['content-type', 'application/atom+xml'], ['authorization', "GoogleLogin auth=#{@token}"]]
405
+ [['content-type', 'application/atom+xml'], ['Authorization', "OAuth #{@token}"]]
489
406
  end
490
407
  end
491
408
 
492
-
493
409
  def multi_part(properties, message)
494
410
  post_body = []
495
411
  post_body << "--#{BOUNDARY}\n"