google_apps 0.4.9 → 0.4.9.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/google_apps/apps_request.rb +77 -0
- data/lib/google_apps/atom/atom.rb +1 -1
- data/lib/google_apps/atom/export.rb +1 -1
- data/lib/google_apps/atom/group.rb +40 -0
- data/lib/google_apps/atom/message_attributes.rb +19 -1
- data/lib/google_apps/atom/nickname.rb +1 -1
- data/lib/google_apps/atom/node.rb +24 -2
- data/lib/google_apps/document_handler.rb +65 -0
- data/lib/google_apps/transport.rb +153 -80
- data/lib/google_apps.rb +2 -0
- metadata +5 -2
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module GoogleApps
|
4
|
+
class AppsRequest
|
5
|
+
attr_reader :uri
|
6
|
+
|
7
|
+
def initialize(verb, uri, headers)
|
8
|
+
@uri = URI uri
|
9
|
+
@ssl = (@uri.scheme == 'https')
|
10
|
+
@http_request = initialize_http(verb)
|
11
|
+
|
12
|
+
set_headers(headers)
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
# send_request does the actual work of sending @http_request as
|
17
|
+
# it is currently constructed.
|
18
|
+
#
|
19
|
+
# send_request
|
20
|
+
#
|
21
|
+
# send_request returns a Net::HTTPResponse object.
|
22
|
+
def send_request
|
23
|
+
Net::HTTP.start(@uri.host, @uri.port, use_ssl: @ssl) do |http|
|
24
|
+
http.request(@http_request)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# add_body sets the body the provided content.
|
30
|
+
#
|
31
|
+
# add_body 'bob'
|
32
|
+
#
|
33
|
+
# add_body returns the content added.
|
34
|
+
def add_body(content)
|
35
|
+
@http_request.body = content
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
|
42
|
+
# intialize_http builds the proper type of HTTP object for the
|
43
|
+
# request. It takes an HTTP verb as it's argument.
|
44
|
+
#
|
45
|
+
# initialize_http :get
|
46
|
+
#
|
47
|
+
# initialize_http returns a Net::HTTP object of the specified type.
|
48
|
+
def initialize_http(verb)
|
49
|
+
build_constant(verb.to_s).new(@uri.request_uri)
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
# build_constant returns the proper constant for the specified
|
54
|
+
# http verb. It takes a HTTP verb as it's argument.
|
55
|
+
#
|
56
|
+
# build_constant :get
|
57
|
+
#
|
58
|
+
# build_constant returns the constant corresponding to the Net::HTTP
|
59
|
+
# class of the specified type.
|
60
|
+
def build_constant(verb)
|
61
|
+
"Net::HTTP::#{verb.capitalize}".split('::').inject(Object) do |context, constant|
|
62
|
+
context.const_get constant
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
# set_headers sets the headers on @http_request. set_headers takes
|
68
|
+
# an array of header/value pairs as it's only argument.
|
69
|
+
#
|
70
|
+
# set_headers [['content-type', 'application/xml']]
|
71
|
+
def set_headers(headers)
|
72
|
+
headers.each do |field, value|
|
73
|
+
@http_request[field] = value
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -44,7 +44,7 @@ module GoogleApps
|
|
44
44
|
|
45
45
|
ENTRY_TAG = ["<atom:entry xmlns:atom=\"#{NAMESPACES[:atom]}\" xmlns:apps=\"#{NAMESPACES[:apps]}\" xmlns:gd=\"#{NAMESPACES[:gd]}\">", '</atom:entry>']
|
46
46
|
|
47
|
-
DOCUMENTS = %w(user export group group_member message_attributes public_key feed)
|
47
|
+
DOCUMENTS = %w(user export group group_member message_attributes public_key feed nickname)
|
48
48
|
|
49
49
|
# The idea is to make document distribution more dynamic.
|
50
50
|
# Might be pointless but it's here for now.
|
@@ -4,6 +4,8 @@ module GoogleApps
|
|
4
4
|
include Atom::Node
|
5
5
|
include Atom::Document
|
6
6
|
|
7
|
+
attr_accessor :id, :name, :description, :permissions
|
8
|
+
|
7
9
|
#ATTRIBUTES = %w(id name description perms).map(&:to_sym)
|
8
10
|
|
9
11
|
def initialize(xml = nil)
|
@@ -49,6 +51,44 @@ module GoogleApps
|
|
49
51
|
@document.root
|
50
52
|
end
|
51
53
|
|
54
|
+
|
55
|
+
def change_value(name, old_value, new_value)
|
56
|
+
find_and_update @document, '//apps:property', { name => [old_value, new_value] }
|
57
|
+
end
|
58
|
+
|
59
|
+
# TODO: This needs to check all attributes of the element
|
60
|
+
def id=(value)
|
61
|
+
@id ? change_value(:value, @id, value) : set_values(id: value)
|
62
|
+
|
63
|
+
@id = value
|
64
|
+
@document = parse(@document)
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
def name=(value)
|
69
|
+
@name ? change_value(:value, @name, value) : set_values(name: value)
|
70
|
+
|
71
|
+
@name = value
|
72
|
+
@document = parse(@document)
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def permissions=(value)
|
77
|
+
@permissions ? change_value(:value, @permissions, value) : set_values(perms: value)
|
78
|
+
|
79
|
+
@permissions = value
|
80
|
+
@document = parse(@document)
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
def description=(value)
|
85
|
+
@description ? change_value(:value, @description, value) : set_values(description: value)
|
86
|
+
|
87
|
+
@description = value
|
88
|
+
@document = parse(@document)
|
89
|
+
end
|
90
|
+
|
91
|
+
|
52
92
|
# to_s returns @document as a String.
|
53
93
|
def to_s
|
54
94
|
@document.to_s
|
@@ -1,11 +1,15 @@
|
|
1
1
|
module GoogleApps
|
2
2
|
module Atom
|
3
3
|
class MessageAttributes
|
4
|
+
attr_reader :labels
|
5
|
+
attr_accessor :property
|
6
|
+
|
4
7
|
def initialize
|
8
|
+
@labels = []
|
5
9
|
@document = Atom::XML::Document.new
|
6
10
|
set_header
|
7
11
|
end
|
8
|
-
|
12
|
+
|
9
13
|
def add_property(prop)
|
10
14
|
property = Atom::XML::Node.new 'apps:mailItemProperty'
|
11
15
|
property['value'] = prop
|
@@ -13,11 +17,25 @@ module GoogleApps
|
|
13
17
|
@document.root << property
|
14
18
|
end
|
15
19
|
|
20
|
+
def property=(value)
|
21
|
+
@property.nil? ? add_property(value) : change_property(value)
|
22
|
+
end
|
23
|
+
|
16
24
|
def add_label(name)
|
17
25
|
label = Atom::XML::Node.new 'apps:label'
|
18
26
|
label['labelName'] = name
|
19
27
|
|
20
28
|
@document.root << label
|
29
|
+
@labels << name
|
30
|
+
end
|
31
|
+
|
32
|
+
def <<(value)
|
33
|
+
add_label(value) unless @labels.include?(value)
|
34
|
+
end
|
35
|
+
|
36
|
+
def remove_label(value)
|
37
|
+
@labels.delete(value)
|
38
|
+
# Need a way to remove a node from the document.
|
21
39
|
end
|
22
40
|
|
23
41
|
def to_s
|
@@ -65,13 +65,24 @@ module GoogleApps
|
|
65
65
|
# update_node document, '/apps:nickname', name: ['Bob', 'Tom']
|
66
66
|
def find_and_update(document, xpath, attributes)
|
67
67
|
document.find(xpath).each do |node|
|
68
|
-
|
69
|
-
|
68
|
+
if node_match?(node, attributes)
|
69
|
+
attributes.each_key do |attrib|
|
70
|
+
node.attributes[attrib.to_s] = attributes[attrib][1]
|
71
|
+
end
|
70
72
|
end
|
71
73
|
end
|
72
74
|
end
|
73
75
|
|
74
76
|
|
77
|
+
# node_match? checks that each value for each specified
|
78
|
+
# attribute matches the specified value.
|
79
|
+
def node_match?(node, attributes)
|
80
|
+
attributes.keys.inject(true) do |result, key|
|
81
|
+
result and node.attributes[key.to_s] == attributes[key][0]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
75
86
|
# get_content returns the content of the specified node.
|
76
87
|
# If multiple nodes match the xpath value get_content
|
77
88
|
# will return the content of the first occurance.
|
@@ -82,6 +93,17 @@ module GoogleApps
|
|
82
93
|
def get_content(document, xpath)
|
83
94
|
document.find(xpath).first.content
|
84
95
|
end
|
96
|
+
|
97
|
+
|
98
|
+
# get_values returns an array of all the value attributes
|
99
|
+
# on elements matching the given key_attrib pair on the
|
100
|
+
# specified element type.
|
101
|
+
def get_values(element, key_attrib, value = 'value')
|
102
|
+
self.find('//' + element).inject([]) do |values, element|
|
103
|
+
values << element.attributes[value] if element.attributes[key_attrib[0]].match key_attrib[1]
|
104
|
+
values
|
105
|
+
end
|
106
|
+
end
|
85
107
|
end
|
86
108
|
end
|
87
109
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module GoogleApps
|
2
|
+
class DocumentHandler
|
3
|
+
attr_accessor :format
|
4
|
+
|
5
|
+
def initialize(args)
|
6
|
+
set_format args[:format]
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
# create_doc creates a document of the specified format
|
11
|
+
# from the given string.
|
12
|
+
def create_doc(text, type = nil)
|
13
|
+
@documents.include?(type) ? doc_of_type(text, type) : unknown_type(text)
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
# unknown_type takes a string and returns a document of
|
18
|
+
# of the corresponding @format.
|
19
|
+
def unknown_type(text)
|
20
|
+
case @format
|
21
|
+
when :atom, :xml
|
22
|
+
Atom::XML::Document.string(text)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
# format= sets the format for the DocumentHandler
|
28
|
+
def format=(format)
|
29
|
+
set_format format
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
# doc_of_type takes a document type and a string and
|
34
|
+
# returns a document of that type in the current format.
|
35
|
+
def doc_of_type(text, type)
|
36
|
+
raise "No #{@format.to_s.capitalize} document of type: #{type}" unless @documents.include?(type.to_s)
|
37
|
+
|
38
|
+
case @format
|
39
|
+
when :atom, :xml
|
40
|
+
GoogleApps::Atom.send(type, text)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
|
49
|
+
# look_up_doc_types returns a list of document types the
|
50
|
+
# library supports in the current format.
|
51
|
+
def look_up_doc_types
|
52
|
+
case @format
|
53
|
+
when :atom, :xml
|
54
|
+
Atom::DOCUMENTS
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
# set_format Sets @format and @documents
|
60
|
+
def set_format(format)
|
61
|
+
@format = format
|
62
|
+
@documents = look_up_doc_types
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'net/http'
|
2
1
|
require 'cgi'
|
3
2
|
require 'openssl'
|
4
3
|
require 'rexml/document'
|
@@ -6,8 +5,9 @@ require 'rexml/document'
|
|
6
5
|
module GoogleApps
|
7
6
|
class Transport
|
8
7
|
attr_reader :request, :response, :domain, :feeds
|
9
|
-
attr_accessor :auth, :user, :group, :nickname, :export
|
8
|
+
attr_accessor :auth, :user, :group, :nickname, :export, :group, :requester, :migration
|
10
9
|
|
10
|
+
SUCCESS_CODES = [200, 201, 202]
|
11
11
|
BOUNDARY = "=AaB03xDFHT8xgg"
|
12
12
|
PAGE_SIZE = {
|
13
13
|
user: 100,
|
@@ -23,6 +23,8 @@ module GoogleApps
|
|
23
23
|
@nickname = targets[:nickname] || "https://apps-apis.google.com/a/feeds/#{domain}/nickname/2.0"
|
24
24
|
@export = targets[:export] || "https://apps-apis.google.com/a/feeds/compliance/audit/mail/export/#{domain}"
|
25
25
|
@domain = domain
|
26
|
+
@requester = AppsRequest || targets[:requester]
|
27
|
+
@doc_handler = DocumentHandler.new format: (targets[:format] || :atom)
|
26
28
|
@token = nil
|
27
29
|
@response = nil
|
28
30
|
@request = nil
|
@@ -39,18 +41,14 @@ module GoogleApps
|
|
39
41
|
# authenticate returns the HTTP response received
|
40
42
|
# from Google
|
41
43
|
def authenticate(account, pass)
|
42
|
-
|
43
|
-
@request = Net::HTTP::Post.new(uri.path)
|
44
|
-
@request.body = auth_body(account, pass)
|
45
|
-
set_headers :auth
|
46
|
-
|
47
|
-
@response = request uri
|
44
|
+
add(@auth, nil, auth_body(account, pass), :auth)
|
48
45
|
|
49
46
|
set_auth_token
|
50
47
|
|
51
48
|
@response
|
52
49
|
end
|
53
50
|
|
51
|
+
|
54
52
|
# request_export performs the GoogleApps API call to
|
55
53
|
# generate a mailbox export. It takes the username
|
56
54
|
# and an GoogleApps::Atom::Export instance as
|
@@ -58,52 +56,77 @@ module GoogleApps
|
|
58
56
|
#
|
59
57
|
# request_export 'username', document
|
60
58
|
#
|
61
|
-
# request_export returns the
|
62
|
-
#
|
59
|
+
# request_export returns the request ID on success or
|
60
|
+
# the HTTP response object on failure.
|
63
61
|
def request_export(username, document)
|
64
|
-
add(@export + "/#{username}", document)
|
62
|
+
result = add(@export + "/#{username}", :export_response, document)
|
63
|
+
|
64
|
+
get_values(result, 'apps:property', ['name', 'requestId'], 'value')[0].to_i
|
65
|
+
#success_response? ? get_values('apps:property', ['name', 'requestId'], 'value')[0].to_i : @response
|
65
66
|
end
|
66
67
|
|
68
|
+
|
67
69
|
# export_status checks the status of a mailbox export
|
68
70
|
# request. It takes the username and the request_id
|
69
71
|
# as arguments
|
70
72
|
#
|
71
73
|
# export_status 'username', 847576
|
72
74
|
#
|
73
|
-
# export_status will return the
|
74
|
-
#
|
75
|
+
# export_status will return the body of the HTTP response
|
76
|
+
# from Google
|
75
77
|
def export_status(username, req_id)
|
76
|
-
get(@export + "/#{username}", req_id)
|
78
|
+
get(@export + "/#{username}", :export_status, req_id)
|
77
79
|
end
|
78
80
|
|
79
|
-
|
80
|
-
|
81
|
+
|
82
|
+
# export_ready? checks the export_status response for the
|
83
|
+
# presence of an apps:property element with a fileUrl name
|
84
|
+
# attribute.
|
85
|
+
#
|
86
|
+
# export_ready? 'lholcomb2', 82834
|
87
|
+
#
|
88
|
+
# export_ready? returns true if there is a fileUrl present
|
89
|
+
# in the response and false if there is no fileUrl present
|
90
|
+
# in the response.
|
91
|
+
def export_ready?(username, req_id)
|
81
92
|
export_status(username, req_id)
|
82
|
-
doc = REXML::Document.new(@response.body)
|
83
|
-
urls = []
|
84
|
-
doc.elements.each('entry/apps:property') do |property|
|
85
|
-
urls << property.attributes['value'] if property.attributes['name'].match 'fileUrl'
|
86
|
-
end
|
87
93
|
|
88
|
-
|
89
|
-
|
94
|
+
!(export_file_urls.empty?)
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
# fetch_export downloads the mailbox export from Google.
|
99
|
+
# It takes a username, request id and a filename as
|
100
|
+
# arguments. If the export consists of more than one file
|
101
|
+
# the file name will have numbers appended to indicate the
|
102
|
+
# piece of the export.
|
103
|
+
#
|
104
|
+
# fetch_export 'lholcomb2', 838382, 'lholcomb2'
|
105
|
+
#
|
106
|
+
# fetch_export reutrns nil in the event that the export is
|
107
|
+
# not yet ready.
|
108
|
+
def fetch_export(username, req_id, filename)
|
109
|
+
if export_ready?(username, req_id)
|
110
|
+
download_export(filename).each_with_index { |url, index| url.gsub!(/.*/, "#{filename}#{index}")}
|
111
|
+
else
|
112
|
+
nil
|
90
113
|
end
|
91
114
|
end
|
92
115
|
|
116
|
+
|
93
117
|
# download makes a get request of the provided url
|
94
118
|
# and writes the body to the provided filename.
|
95
119
|
#
|
96
120
|
# download 'url', 'save_file'
|
97
121
|
def download(url, filename)
|
98
|
-
|
99
|
-
@request = Net::HTTP::Get.new uri.path
|
100
|
-
set_headers :user
|
122
|
+
@request = @requester.new :get, URI(url), headers(:other)
|
101
123
|
|
102
124
|
File.open(filename, "w") do |file|
|
103
|
-
file.puts request
|
125
|
+
file.puts @request.send_request.body
|
104
126
|
end
|
105
127
|
end
|
106
128
|
|
129
|
+
|
107
130
|
# get is a generic target for method_missing. It is
|
108
131
|
# intended to handle the general case of retrieving a
|
109
132
|
# record from the Google Apps Domain. It takes an API
|
@@ -112,13 +135,13 @@ module GoogleApps
|
|
112
135
|
# get 'endpoint', 'username'
|
113
136
|
#
|
114
137
|
# get returns the HTTP response received from Google.
|
115
|
-
def get(endpoint, id = nil)
|
116
|
-
# TODO: Need to handle <link rel='next' for pagination if wanting all users
|
138
|
+
def get(endpoint, type, id = nil)
|
117
139
|
id ? uri = URI(endpoint + build_id(id)) : uri = URI(endpoint)
|
118
|
-
@request =
|
119
|
-
|
140
|
+
@request = @requester.new :get, uri, headers(:other)
|
141
|
+
|
142
|
+
@response = @request.send_request
|
120
143
|
|
121
|
-
|
144
|
+
process_response(type)
|
122
145
|
end
|
123
146
|
|
124
147
|
|
@@ -153,20 +176,13 @@ module GoogleApps
|
|
153
176
|
#
|
154
177
|
# get_all returns the HTTP response received from Google.
|
155
178
|
def get_all(type, options = {})
|
156
|
-
@feeds,
|
157
|
-
type = type
|
158
|
-
type.gsub!(/\w*s$/) { |match| match[0..-2] }
|
179
|
+
@feeds, page = [], 0
|
180
|
+
type = normalize_type type
|
159
181
|
|
160
182
|
options[:limit] ? limit = options[:limit] : limit = 1000000
|
161
|
-
options[:start] ? get(instance_variable_get("@#{type}") + "?#{start_query(type)}=#{options[:start]}") : get(instance_variable_get("@#{type}"))
|
183
|
+
options[:start] ? get(instance_variable_get("@#{type}") + "?#{start_query(type)}=#{options[:start]}", :feed) : get(instance_variable_get("@#{type}"), :feed)
|
162
184
|
|
163
|
-
|
164
|
-
current_page += 1
|
165
|
-
|
166
|
-
while (@feeds.last.next_page) and (current_page * PAGE_SIZE[:user] < limit)
|
167
|
-
get_next_page
|
168
|
-
current_page += 1
|
169
|
-
end
|
185
|
+
fetch_feed(page, limit)
|
170
186
|
|
171
187
|
@response
|
172
188
|
end
|
@@ -180,7 +196,7 @@ module GoogleApps
|
|
180
196
|
#
|
181
197
|
# add_member_to returns the response received from Google.
|
182
198
|
def add_member_to(group_id, document)
|
183
|
-
add(@group + "/#{group_id}/member", document)
|
199
|
+
add(@group + "/#{group_id}/member", nil, document)
|
184
200
|
end
|
185
201
|
|
186
202
|
|
@@ -214,13 +230,15 @@ module GoogleApps
|
|
214
230
|
# add 'endpoint', document
|
215
231
|
#
|
216
232
|
# add returns the HTTP response received from Google.
|
217
|
-
def add(endpoint, document)
|
233
|
+
def add(endpoint, type, document, header_type = nil)
|
234
|
+
header_type = :others unless header_type
|
218
235
|
uri = URI(endpoint)
|
219
|
-
@request =
|
220
|
-
@request.
|
221
|
-
|
236
|
+
@request = @requester.new :post, uri, headers(header_type)
|
237
|
+
@request.add_body document.to_s
|
238
|
+
|
239
|
+
@response = @request.send_request
|
222
240
|
|
223
|
-
|
241
|
+
process_response type
|
224
242
|
end
|
225
243
|
|
226
244
|
# update is a generic target for method_missing. It is
|
@@ -232,14 +250,14 @@ module GoogleApps
|
|
232
250
|
# update 'endpoint', document
|
233
251
|
#
|
234
252
|
# update returns the HTTP response received from Google
|
235
|
-
def update(endpoint, target, document)
|
236
|
-
# TODO: Username needs to come from somewhere for uri
|
253
|
+
def update(endpoint, type, target, document)
|
237
254
|
uri = URI(endpoint + "/#{target}")
|
238
|
-
@request =
|
239
|
-
@request.
|
240
|
-
|
255
|
+
@request = @requester.new :put, uri, headers(:other)
|
256
|
+
@request.add_body document.to_s
|
257
|
+
|
258
|
+
@response = @request.send_request
|
241
259
|
|
242
|
-
|
260
|
+
process_response type
|
243
261
|
end
|
244
262
|
|
245
263
|
# delete is a generic target for method_missing. It is
|
@@ -252,10 +270,9 @@ module GoogleApps
|
|
252
270
|
# delete returns the HTTP response received from Google.
|
253
271
|
def delete(endpoint, id)
|
254
272
|
uri = URI(endpoint + "/#{id}")
|
255
|
-
@request =
|
256
|
-
set_headers :user
|
273
|
+
@request = @requester.new :delete, uri, headers(:other)
|
257
274
|
|
258
|
-
@response = request
|
275
|
+
@response = @request.send_request
|
259
276
|
end
|
260
277
|
|
261
278
|
# migration performs mail migration from a local
|
@@ -267,28 +284,26 @@ module GoogleApps
|
|
267
284
|
#
|
268
285
|
# migrate returns the HTTP response received from Google.
|
269
286
|
def migrate(username, properties, message)
|
270
|
-
|
271
|
-
@request
|
272
|
-
@request.body = multi_part(properties.to_s, message)
|
273
|
-
set_headers :migrate
|
287
|
+
@request = @requester.new(:post, URI(@migration + "/#{username}/mail"), headers(:migration))
|
288
|
+
@request.add_body multi_part(properties.to_s, message)
|
274
289
|
|
275
|
-
@
|
290
|
+
@request.send_request
|
276
291
|
end
|
277
292
|
|
278
293
|
|
279
|
-
|
294
|
+
|
280
295
|
def method_missing(name, *args)
|
281
296
|
super unless name.match /([a-z]*)_([a-z]*)/
|
282
297
|
|
283
298
|
case $1
|
284
299
|
when "new", "add"
|
285
|
-
self.send(:add, instance_variable_get("@#{$2}"), *args)
|
300
|
+
self.send(:add, instance_variable_get("@#{$2}"), $2, *args)
|
286
301
|
when "delete"
|
287
302
|
self.send(:delete, instance_variable_get("@#{$2}"), *args)
|
288
303
|
when "update"
|
289
|
-
self.send(:update, instance_variable_get("@#{$2}"), *args)
|
304
|
+
self.send(:update, instance_variable_get("@#{$2}"), $2, *args)
|
290
305
|
when "get"
|
291
|
-
self.send(:get, instance_variable_get("@#{$2}"), *args)
|
306
|
+
self.send(:get, instance_variable_get("@#{$2}"), $2, *args)
|
292
307
|
else
|
293
308
|
super
|
294
309
|
end
|
@@ -318,6 +333,43 @@ module GoogleApps
|
|
318
333
|
end
|
319
334
|
|
320
335
|
|
336
|
+
# export_file_urls searches @response for any apps:property elements with a
|
337
|
+
# fileUrl name attribute and returns an array of the values.
|
338
|
+
def export_file_urls
|
339
|
+
Atom::XML::Document.string(@response.body).find('//apps:property').inject([]) do |urls, prop|
|
340
|
+
urls << prop.attributes['value'] if prop.attributes['name'].match 'fileUrl'
|
341
|
+
urls
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
|
346
|
+
def download_export(filename)
|
347
|
+
export_file_urls.each_with_index do |url, index|
|
348
|
+
download(url, filename + "#{index}")
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
|
353
|
+
# process_response takes the HTTPResponse and either returns a
|
354
|
+
# document of the specified type or in the event of an error it
|
355
|
+
# returns the HTTPResponse.
|
356
|
+
def process_response(doc_type = nil)
|
357
|
+
case doc_type
|
358
|
+
when nil
|
359
|
+
success_response? ? true : raise("Error: #{response.code}, #{response.message}")
|
360
|
+
else
|
361
|
+
success_response? ? @doc_handler.create_doc(@response.body, doc_type) : raise("Error: #{response.code}, #{response.message}")
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
|
366
|
+
# error_response? checks to see if Google Responded with a success
|
367
|
+
# code.
|
368
|
+
def success_response?
|
369
|
+
SUCCESS_CODES.include?(@response.code.to_i)
|
370
|
+
end
|
371
|
+
|
372
|
+
|
321
373
|
# Grab the auth token from the response body
|
322
374
|
def set_auth_token
|
323
375
|
@response.body.split("\n").grep(/auth=(.*)/i)
|
@@ -326,12 +378,26 @@ module GoogleApps
|
|
326
378
|
end
|
327
379
|
|
328
380
|
|
381
|
+
# get_next_page retrieves the next page in the response.
|
329
382
|
def get_next_page
|
330
383
|
get @feeds.last.next_page
|
331
384
|
add_feed
|
332
385
|
end
|
333
386
|
|
334
387
|
|
388
|
+
# fetch_feed retrieves the remaining pages in the request.
|
389
|
+
# It takes a page and a limit as arguments.
|
390
|
+
def fetch_feed(page, limit)
|
391
|
+
add_feed
|
392
|
+
page += 1
|
393
|
+
|
394
|
+
while (@feeds.last.next_page) and (page * PAGE_SIZE[:user] < limit)
|
395
|
+
get_next_page
|
396
|
+
page += 1
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
|
335
401
|
# start_query builds the value for the starting point
|
336
402
|
# query string used for retrieving batches of objects
|
337
403
|
# from Google.
|
@@ -345,32 +411,39 @@ module GoogleApps
|
|
345
411
|
end
|
346
412
|
|
347
413
|
|
414
|
+
def normalize_type(type)
|
415
|
+
type.to_s.gsub!(/\w*s$/) { |match| match[0..-2] }
|
416
|
+
end
|
417
|
+
|
418
|
+
|
348
419
|
# add_feed adds a feed to the @feeds array.
|
349
420
|
def add_feed
|
350
421
|
@feeds << GoogleApps::Atom.feed(@response.body)
|
351
422
|
end
|
352
423
|
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
424
|
+
# get_values returns an array of all the value attributes
|
425
|
+
# on elements matching the given key_attrib pair on the
|
426
|
+
# specified element type.
|
427
|
+
def get_values(document, element, key_attrib, value = 'value')
|
428
|
+
document.find('//' + element).inject([]) do |values, element|
|
429
|
+
values << element.attributes[value] if element.attributes[key_attrib[0]].match key_attrib[1]
|
430
|
+
values
|
358
431
|
end
|
359
432
|
end
|
360
433
|
|
361
|
-
|
362
|
-
|
434
|
+
|
435
|
+
def headers(category)
|
436
|
+
case category
|
363
437
|
when :auth
|
364
|
-
|
365
|
-
when :
|
366
|
-
|
367
|
-
@request['authorization'] = "GoogleLogin auth=#{@token}"
|
438
|
+
[['content-type', 'application/x-www-form-urlencoded']]
|
439
|
+
when :migration
|
440
|
+
[['content-type', "multipart/related; boundary=\"#{BOUNDARY}\""], ['authorization', "GoogleLogin auth=#{@token}"]]
|
368
441
|
else
|
369
|
-
|
370
|
-
@request['authorization'] = "GoogleLogin auth=#{@token}"
|
442
|
+
[['content-type', 'application/atom+xml'], ['authorization', "GoogleLogin auth=#{@token}"]]
|
371
443
|
end
|
372
444
|
end
|
373
445
|
|
446
|
+
|
374
447
|
def multi_part(properties, message)
|
375
448
|
post_body = []
|
376
449
|
post_body << "--#{BOUNDARY}\n"
|
data/lib/google_apps.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
require 'google_apps/apps_request'
|
1
2
|
require 'google_apps/transport'
|
2
3
|
require 'google_apps/atom/atom'
|
3
4
|
require 'google_apps/atom/node'
|
4
5
|
require 'google_apps/atom/document'
|
6
|
+
require 'google_apps/document_handler'
|
5
7
|
require 'google_apps/atom/feed'
|
6
8
|
require 'google_apps/atom/user'
|
7
9
|
require 'google_apps/atom/group'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: google_apps
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.9
|
4
|
+
version: 0.4.9.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-08-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: libxml-ruby
|
@@ -33,6 +33,7 @@ executables: []
|
|
33
33
|
extensions: []
|
34
34
|
extra_rdoc_files: []
|
35
35
|
files:
|
36
|
+
- lib/google_apps/apps_request.rb
|
36
37
|
- lib/google_apps/atom/atom.rb
|
37
38
|
- lib/google_apps/atom/document.rb
|
38
39
|
- lib/google_apps/atom/export.rb
|
@@ -45,6 +46,7 @@ files:
|
|
45
46
|
- lib/google_apps/atom/node.rb
|
46
47
|
- lib/google_apps/atom/public_key.rb
|
47
48
|
- lib/google_apps/atom/user.rb
|
49
|
+
- lib/google_apps/document_handler.rb
|
48
50
|
- lib/google_apps/transport.rb
|
49
51
|
- lib/google_apps.rb
|
50
52
|
homepage: https://github.com/LeakyBucket/google_apps
|
@@ -72,3 +74,4 @@ signing_key:
|
|
72
74
|
specification_version: 3
|
73
75
|
summary: Google Apps APIs
|
74
76
|
test_files: []
|
77
|
+
has_rdoc:
|