google_apps 0.5 → 0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -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"