google_apps 0.4.9 → 0.4.9.1
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.
- 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:
|