google_apps_api 0.1.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -1
- data/VERSION +1 -1
- data/google_apps_api.gemspec +9 -4
- data/lib/config/calendar.yml +36 -9
- data/lib/config/contacts.yml +35 -0
- data/lib/config/profiles.yml +1 -0
- data/lib/config/provisioning.yml +10 -3
- data/lib/google_apps_api/base_api.rb +120 -36
- data/lib/google_apps_api/calendar.rb +305 -30
- data/lib/google_apps_api/contacts.rb +83 -53
- data/lib/google_apps_api/provisioning.rb +66 -242
- data/test/example_connection_config.yml +4 -0
- data/test/google_apps_api_base_api_test.rb +76 -0
- data/test/google_apps_api_calendar_test.rb +225 -54
- data/test/google_apps_api_contacts_test.rb +103 -27
- data/test/google_apps_api_off_domain_calendar_test.rb +27 -0
- data/test/google_apps_api_provisioning_test.rb +3 -4
- data/test/test_helper.rb +5 -0
- metadata +9 -4
- data/private/gapps-config.yml +0 -5
- data/private/userscalendars.xml +0 -113
@@ -10,41 +10,20 @@ module GoogleAppsApi #:nodoc:
|
|
10
10
|
super(:provisioning, *args)
|
11
11
|
end
|
12
12
|
|
13
|
-
def retrieve_user(
|
14
|
-
|
13
|
+
def retrieve_user(user, *args)
|
14
|
+
username = user.kind_of?(UserEntity) ? user.id : user
|
15
|
+
|
16
|
+
options = args.extract_options!.merge(:username => username)
|
17
|
+
request(:retrieve_user, options)
|
15
18
|
end
|
16
19
|
|
17
20
|
|
18
|
-
def retrieve_all_users
|
19
|
-
|
21
|
+
def retrieve_all_users(*args)
|
22
|
+
options = args.extract_options!
|
23
|
+
request(:retrieve_all_users, options)
|
20
24
|
end
|
21
|
-
|
22
|
-
|
23
|
-
# # ex :
|
24
|
-
# # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
|
25
|
-
# # list= myapps.retrieve_page_of_users("jsmtih")
|
26
|
-
# # list.each{ |user| puts user.username}
|
27
|
-
# def retrieve_page_of_users(start_username)
|
28
|
-
# param='?startUsername='+start_username
|
29
|
-
# response = request(:user_retrieve_all,param,@headers)
|
30
|
-
# user_feed = Feed.new(response.elements["feed"], UserEntry)
|
31
|
-
# end
|
32
|
-
#
|
33
|
-
#
|
34
|
-
# def contacts_retrieve_all()
|
35
|
-
# response = request(:contacts_retrieve_all,nil, @headers)
|
36
|
-
# end
|
37
|
-
#
|
38
|
-
# Creates an account in your domain, returns a UserEntry instance
|
39
|
-
# params :
|
40
|
-
# username, given_name, family_name and password are required
|
41
|
-
# passwd_hash_function (optional) : nil (default) or "SHA-1"
|
42
|
-
# quota (optional) : nil (default) or integer for limit in MB
|
43
|
-
# ex :
|
44
|
-
# myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
|
45
|
-
# user = myapps.create('jsmith', 'John', 'Smith', 'p455wD')
|
46
|
-
#
|
47
|
-
# By default, a new user must change his password at first login. Please use update_user if you want to change this just after the creation.
|
25
|
+
|
26
|
+
|
48
27
|
def create_user(username, *args)
|
49
28
|
options = args.extract_options!
|
50
29
|
options.each { |k,v| options[k] = escapeXML(v)}
|
@@ -52,243 +31,88 @@ module GoogleAppsApi #:nodoc:
|
|
52
31
|
res = <<-DESCXML
|
53
32
|
<?xml version="1.0" encoding="UTF-8"?>
|
54
33
|
<atom:entry xmlns:atom="http://www.w3.org/2005/Atom"
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
34
|
+
xmlns:apps="http://schemas.google.com/apps/2006">
|
35
|
+
<atom:category scheme="http://schemas.google.com/g/2005#kind"
|
36
|
+
term="http://schemas.google.com/apps/2006#user"/>
|
37
|
+
<apps:login userName="#{escapeXML(username)}"
|
38
|
+
password="#{options[:password]}" suspended="false"/>
|
39
|
+
<apps:name familyName="#{options[:family_name]}" givenName="#{options[:given_name]}"/>
|
61
40
|
</atom:entry>
|
62
41
|
|
63
42
|
DESCXML
|
64
43
|
|
65
|
-
|
66
|
-
request(:create_user, :body => res.strip)
|
44
|
+
|
45
|
+
request(:create_user, options.merge(:body => res.strip))
|
67
46
|
end
|
68
|
-
|
69
|
-
|
70
|
-
# # params :
|
71
|
-
# # username is required and can't be updated.
|
72
|
-
# # given_name and family_name are required, may be updated.
|
73
|
-
# # if set to nil, every other parameter won't update the attribute.
|
74
|
-
# # passwd_hash_function : string "SHA-1", "MD5" or nil (default)
|
75
|
-
# # admin : string "true" or string "false" or nil (no boolean : true or false).
|
76
|
-
# # suspended : string "true" or string "false" or nil (no boolean : true or false)
|
77
|
-
# # change_passwd : string "true" or string "false" or nil (no boolean : true or false)
|
78
|
-
# # quota : limit en MB, ex : string "2048"
|
79
|
-
# # ex :
|
80
|
-
# # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
|
81
|
-
# # user = myapps.update('jsmith', 'John', 'Smith', nil, nil, "true", nil, "true", nil)
|
82
|
-
# # puts user.admin => "true"
|
47
|
+
|
48
|
+
|
83
49
|
def update_user(username, *args)
|
84
50
|
options = args.extract_options!
|
85
51
|
options.each { |k,v| options[k] = escapeXML(v)}
|
86
|
-
|
52
|
+
|
87
53
|
res = <<-DESCXML
|
88
54
|
<?xml version="1.0" encoding="UTF-8"?>
|
89
55
|
<atom:entry xmlns:atom="http://www.w3.org/2005/Atom"
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
56
|
+
xmlns:apps="http://schemas.google.com/apps/2006">
|
57
|
+
<atom:category scheme="http://schemas.google.com/g/2005#kind"
|
58
|
+
term="http://schemas.google.com/apps/2006#user"/>
|
59
|
+
<apps:name familyName="#{options[:family_name]}" givenName="#{options[:given_name]}"/>
|
94
60
|
</atom:entry>
|
95
|
-
|
61
|
+
|
96
62
|
DESCXML
|
97
|
-
request(:update_user, :username => username, :body => res.strip)
|
63
|
+
request(:update_user, options.merge(:username => username, :body => res.strip))
|
98
64
|
end
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
# # user = myapps.rename_user('jsmith','jdoe')
|
105
|
-
# #
|
106
|
-
# # It is recommended to log out rhe user from all browser sessions and service before renaming.
|
107
|
-
# # Once renamed, the old username becomes a nickname of the new username.
|
108
|
-
# # Note from Google: Google Talk will lose all remembered chat invitations after renaming.
|
109
|
-
# # The user must request permission to chat with friends again.
|
110
|
-
# # Also, when a user is renamed, the old username is retained as a nickname to ensure continuous mail delivery in the case of email forwarding settings.
|
111
|
-
# # To remove the nickname, you should issue an HTTP DELETE to the nicknames feed after renaming.
|
112
|
-
# def rename_user(username, new_username)
|
113
|
-
# msg = RequestMessage.new
|
114
|
-
# msg.about_login(new_username)
|
115
|
-
# msg.add_path('https://'+@@google_host+@action[:user_rename][:path]+username)
|
116
|
-
# response = request(:user_update,username,@headers, msg.to_s)
|
117
|
-
# end
|
118
|
-
#
|
119
|
-
# # Suspends an account in your domain, returns a UserEntry instance
|
120
|
-
# # ex :
|
121
|
-
# # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
|
122
|
-
# # user = myapps.suspend('jsmith')
|
123
|
-
# # puts user.suspended => "true"
|
124
|
-
# def suspend_user(username)
|
125
|
-
# msg = RequestMessage.new
|
126
|
-
# msg.about_login(username,nil,nil,nil,"true")
|
127
|
-
# msg.add_path('https://'+@@google_host+@action[:user_update][:path]+username)
|
128
|
-
# response = request(:user_update,username,@headers, msg.to_s)
|
129
|
-
# user_entry = UserEntry.new(response.elements["entry"])
|
130
|
-
# end
|
131
|
-
#
|
132
|
-
# # Restores a suspended account in your domain, returns a UserEntry instance
|
133
|
-
# # ex :
|
134
|
-
# # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
|
135
|
-
# # user = myapps.restore('jsmith')
|
136
|
-
# # puts user.suspended => "false"
|
137
|
-
# def restore_user(username)
|
138
|
-
# msg = RequestMessage.new
|
139
|
-
# msg.about_login(username,nil,nil,nil,"false")
|
140
|
-
# msg.add_path('https://'+@@google_host+@action[:user_update][:path]+username)
|
141
|
-
# response = request(:user_update,username,@headers, msg.to_s)
|
142
|
-
# user_entry = UserEntry.new(response.elements["entry"])
|
143
|
-
# end
|
144
|
-
#
|
145
|
-
# # Deletes an account in your domain
|
146
|
-
# # ex :
|
147
|
-
# # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
|
148
|
-
# # myapps.delete('jsmith')
|
149
|
-
def delete_user(username)
|
150
|
-
response = request(:delete_user, :username => username)
|
65
|
+
|
66
|
+
|
67
|
+
def delete_user(username, *args)
|
68
|
+
options = args.extract_options!.merge(:username => username)
|
69
|
+
request(:delete_user, options)
|
151
70
|
end
|
152
71
|
|
153
72
|
|
154
73
|
end
|
155
74
|
|
156
|
-
|
157
|
-
#
|
158
|
-
#
|
159
|
-
# class RequestMessage < Document #:nodoc:
|
160
|
-
# # Request message constructor.
|
161
|
-
# # parameter type : "user", "nickname" or "emailList"
|
162
|
-
#
|
163
|
-
# # creates the object and initiates the construction
|
164
|
-
# def initialize
|
165
|
-
# super '<?xml version="1.0" encoding="UTF-8"?>'
|
166
|
-
# self.add_element "atom:entry", {"xmlns:apps" => "http://schemas.google.com/apps/2006",
|
167
|
-
# "xmlns:gd" => "http://schemas.google.com/g/2005",
|
168
|
-
# "xmlns:atom" => "http://www.w3.org/2005/Atom"}
|
169
|
-
#
|
170
|
-
# self.elements["atom:entry"].add_element "atom:category", {"scheme" => "http://schemas.google.com/g/2005#kind"}
|
171
|
-
#
|
172
|
-
# end
|
173
|
-
#
|
174
|
-
# # adds <atom:id> element in the message body. Url is inserted as a text.
|
175
|
-
# def add_path(url)
|
176
|
-
# self.elements["atom:entry"].add_element "atom:id"
|
177
|
-
# self.elements["atom:entry/atom:id"].text = url
|
178
|
-
# end
|
179
|
-
#
|
180
|
-
# # adds <apps:emailList> element in the message body.
|
181
|
-
# def about_email_list(email_list)
|
182
|
-
# self.elements["atom:entry/atom:category"].add_attribute("term", "http://schemas.google.com/apps/2006#emailList")
|
183
|
-
# self.elements["atom:entry"].add_element "apps:emailList", {"name" => email_list }
|
184
|
-
# end
|
185
|
-
#
|
186
|
-
# # adds <apps:property> element in the message body for a group.
|
187
|
-
# def about_group(group_id, properties)
|
188
|
-
# self.elements["atom:entry/atom:category"].add_attribute("term", "http://schemas.google.com/apps/2006#emailList")
|
189
|
-
# self.elements["atom:entry"].add_element "apps:property", {"name" => "groupId", "value" => group_id }
|
190
|
-
# self.elements["atom:entry"].add_element "apps:property", {"name" => "groupName", "value" => properties[0] }
|
191
|
-
# self.elements["atom:entry"].add_element "apps:property", {"name" => "description", "value" => properties[1] }
|
192
|
-
# self.elements["atom:entry"].add_element "apps:property", {"name" => "emailPermission", "value" => properties[2] }
|
193
|
-
# end
|
194
|
-
#
|
195
|
-
# # adds <apps:property> element in the message body for a member.
|
196
|
-
# def about_member(email_address)
|
197
|
-
# self.elements["atom:entry/atom:category"].add_attribute("term", "http://schemas.google.com/apps/2006#user")
|
198
|
-
# self.elements["atom:entry"].add_element "apps:property", {"name" => "memberId", "value" => email_address }
|
199
|
-
# end
|
200
|
-
#
|
201
|
-
# # adds <apps:property> element in the message body for an owner.
|
202
|
-
# def about_owner(email_address)
|
203
|
-
# self.elements["atom:entry/atom:category"].add_attribute("term", "http://schemas.google.com/apps/2006#user")
|
204
|
-
# self.elements["atom:entry"].add_element "apps:property", {"name" => "email", "value" => email_address }
|
205
|
-
# end
|
206
|
-
#
|
207
|
-
#
|
208
|
-
# # adds <apps:login> element in the message body.
|
209
|
-
# # warning : if valued admin, suspended, or change_passwd_at_next_login must be the STRINGS "true" or "false", not the boolean true or false
|
210
|
-
# # when needed to construct the message, should always been used before other "about_" methods so that the category tag can be overwritten
|
211
|
-
# # only values permitted for hash_function_function_name : "SHA-1", "MD5" or nil
|
212
|
-
# def about_login(user_name, passwd=nil, hash_function_name=nil, admin=nil, suspended=nil, change_passwd_at_next_login=nil)
|
213
|
-
# self.elements["atom:entry/atom:category"].add_attribute("term", "http://schemas.google.com/apps/2006#user")
|
214
|
-
# self.elements["atom:entry"].add_element "apps:login", {"userName" => user_name }
|
215
|
-
# self.elements["atom:entry/apps:login"].add_attribute("password", passwd) if not passwd.nil?
|
216
|
-
# self.elements["atom:entry/apps:login"].add_attribute("hashFunctionName", hash_function_name) if not hash_function_name.nil?
|
217
|
-
# self.elements["atom:entry/apps:login"].add_attribute("admin", admin) if not admin.nil?
|
218
|
-
# self.elements["atom:entry/apps:login"].add_attribute("suspended", suspended) if not suspended.nil?
|
219
|
-
# self.elements["atom:entry/apps:login"].add_attribute("changePasswordAtNextLogin", change_passwd_at_next_login) if not change_passwd_at_next_login.nil?
|
220
|
-
# return self
|
221
|
-
# end
|
222
|
-
#
|
223
|
-
# # adds <apps:quota> in the message body.
|
224
|
-
# # limit in MB: integer
|
225
|
-
# def about_quota(limit)
|
226
|
-
# self.elements["atom:entry"].add_element "apps:quota", {"limit" => limit }
|
227
|
-
# return self
|
228
|
-
# end
|
229
|
-
#
|
230
|
-
# # adds <apps:name> in the message body.
|
231
|
-
# def about_name(family_name, given_name)
|
232
|
-
# self.elements["atom:entry"].add_element "apps:name", {"familyName" => family_name, "givenName" => given_name }
|
233
|
-
# return self
|
234
|
-
# end
|
235
|
-
#
|
236
|
-
# # adds <apps:nickname> in the message body.
|
237
|
-
# def about_nickname(name)
|
238
|
-
# self.elements["atom:entry/atom:category"].add_attribute("term", "http://schemas.google.com/apps/2006#nickname")
|
239
|
-
# self.elements["atom:entry"].add_element "apps:nickname", {"name" => name}
|
240
|
-
# return self
|
241
|
-
# end
|
242
|
-
#
|
243
|
-
# # adds <gd:who> in the message body.
|
244
|
-
# def about_who(email)
|
245
|
-
# self.elements["atom:entry"].add_element "gd:who", {"email" => email }
|
246
|
-
# return self
|
247
|
-
# end
|
248
|
-
#
|
249
|
-
# end
|
250
|
-
# end
|
251
75
|
end
|
252
|
-
|
253
76
|
|
254
77
|
|
255
|
-
class
|
256
|
-
attr_accessor :given_name, :family_name, :username, :suspended, :ip_whitelisted, :admin, :change_password_at_next_login, :agreed_to_terms, :quota_limit
|
257
|
-
|
258
|
-
def initialize(
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
@
|
263
|
-
@
|
264
|
-
@
|
265
|
-
|
266
|
-
@
|
267
|
-
@
|
268
|
-
@
|
78
|
+
class UserEntity < Entity
|
79
|
+
attr_accessor :given_name, :family_name, :username, :suspended, :ip_whitelisted, :admin, :change_password_at_next_login, :agreed_to_terms, :quota_limit, :domain
|
80
|
+
|
81
|
+
def initialize(*args)
|
82
|
+
options = args.extract_options!
|
83
|
+
if (_xml = options[:xml])
|
84
|
+
xml = _xml.at_css("entry") || _xml
|
85
|
+
@kind = "user"
|
86
|
+
@id = xml.at_css("apps|login").attribute("userName").content
|
87
|
+
@domain = xml.at_css("id").content.gsub(/^.+\/feeds\/([^\/]+)\/.+$/,"\\1")
|
88
|
+
|
89
|
+
@family_name = xml.at_css("apps|name").attribute("familyName").content
|
90
|
+
@given_name = xml.at_css("apps|name").attribute("givenName").content
|
91
|
+
@suspended = xml.at_css("apps|login").attribute("suspended").content
|
92
|
+
@ip_whitelisted = xml.at_css("apps|login").attribute("ipWhitelisted").content
|
93
|
+
@admin = xml.at_css("apps|login").attribute("admin").content
|
94
|
+
@change_password_at_next_login = xml.at_css("apps|login").attribute("changePasswordAtNextLogin").content
|
95
|
+
@agreed_to_terms = xml.at_css("apps|login").attribute("agreedToTerms").content
|
96
|
+
@quota_limit = xml.at_css("apps|quota").attribute("limit").content
|
97
|
+
else
|
98
|
+
if args.first.kind_of?(String)
|
99
|
+
super(:user => args.first)
|
100
|
+
else
|
101
|
+
super(options.merge(:kind => "user"))
|
102
|
+
end
|
269
103
|
end
|
270
104
|
end
|
271
|
-
|
272
|
-
def
|
273
|
-
|
105
|
+
|
106
|
+
def entity_for_base_calendar
|
107
|
+
CalendarEntity.new(self.full_id)
|
274
108
|
end
|
275
|
-
|
276
|
-
def
|
277
|
-
|
109
|
+
|
110
|
+
def get_base_calendar(c_api, *args)
|
111
|
+
c_api.retrieve_calendar_for_user(self.entity_for_base_calendar, self, *args)
|
278
112
|
end
|
279
|
-
|
280
|
-
def
|
281
|
-
|
113
|
+
|
114
|
+
def get_calendars(c_api, *args)
|
115
|
+
c_api.retrieve_calendars_for_user(self, *args)
|
282
116
|
end
|
283
|
-
#
|
284
|
-
# def add_message
|
285
|
-
# Nokogiri::XML::Builder.new { |xml|
|
286
|
-
# xml.entry(:xmlns => "http://www.w3.org/2005/Atom") {
|
287
|
-
# xml.id_ {
|
288
|
-
# xml.text id.to_s
|
289
|
-
# }
|
290
|
-
# }
|
291
|
-
# }.to_xml
|
292
|
-
# end
|
293
117
|
end
|
294
118
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class GoogleAppsApiBaseApiTest < Test::Unit::TestCase
|
4
|
+
include GoogleAppsApi
|
5
|
+
|
6
|
+
context "given an example entity" do
|
7
|
+
setup do
|
8
|
+
@en = Entity.new(:kind => "user", :id => "test1", :domain => "ocelot.cul.columbia.edu")
|
9
|
+
@c_en = Entity.new(:kind => "calendar", :id => "js235", :domain => "ocelot.cul.columbia.edu")
|
10
|
+
@d_en = Entity.new(:kind => "domain", :id => "ocelot.cul.columbia.edu")
|
11
|
+
@co_en = Entity.new(:kind => "contact", :id => "12345")
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
should "reject items without kind and id" do
|
16
|
+
assert_raises ArgumentError do
|
17
|
+
Entity.new(:id => "test1", :domain => "oc")
|
18
|
+
end
|
19
|
+
|
20
|
+
assert_raises ArgumentError do
|
21
|
+
Entity.new(:kind => "user", :domain => "oc")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
should "accept a user argument" do
|
26
|
+
u_en = Entity.new(:user => "test1", :domain => "ocelot.cul.columbia.edu")
|
27
|
+
|
28
|
+
assert_equal @en, u_en
|
29
|
+
end
|
30
|
+
|
31
|
+
should "split domains out" do
|
32
|
+
u_en = Entity.new(:user => "test1@ocelot.cul.columbia.edu")
|
33
|
+
|
34
|
+
assert_equal @en, u_en
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
should "split encoded domains out" do
|
39
|
+
u_en = Entity.new(:user => "test1%40ocelot.cul.columbia.edu")
|
40
|
+
|
41
|
+
assert_equal @en, u_en
|
42
|
+
end
|
43
|
+
|
44
|
+
should "be able to display the encoded and non-encoded versions" do
|
45
|
+
assert_equal "test1@ocelot.cul.columbia.edu", @en.full_id
|
46
|
+
assert_equal "test1%40ocelot.cul.columbia.edu", @en.full_id_escaped
|
47
|
+
assert_equal "ocelot.cul.columbia.edu", @d_en.full_id
|
48
|
+
assert_equal "ocelot.cul.columbia.edu", @d_en.full_id_escaped
|
49
|
+
end
|
50
|
+
|
51
|
+
should "be able to create user entities" do
|
52
|
+
assert_equal @en, UserEntity.new("test1@ocelot.cul.columbia.edu")
|
53
|
+
assert_equal @en, UserEntity.new("test1%40ocelot.cul.columbia.edu")
|
54
|
+
assert_equal @en, UserEntity.new(:id => "test1", :domain => "ocelot.cul.columbia.edu")
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
should "be able to create calendar entities" do
|
59
|
+
assert_equal @c_en, CalendarEntity.new("js235@ocelot.cul.columbia.edu")
|
60
|
+
assert_equal @c_en, CalendarEntity.new("js235%40ocelot.cul.columbia.edu")
|
61
|
+
assert_equal @c_en, CalendarEntity.new(:id => "js235", :domain => "ocelot.cul.columbia.edu")
|
62
|
+
end
|
63
|
+
|
64
|
+
should "be able to derive a calendar entity from a user entity" do
|
65
|
+
assert_equal @c_en, UserEntity.new("js235@ocelot.cul.columbia.edu").entity_for_base_calendar
|
66
|
+
end
|
67
|
+
|
68
|
+
should "be able to display the qualified id, escape and nonescaped" do
|
69
|
+
assert_equal "user:test1@ocelot.cul.columbia.edu", @en.qualified_id
|
70
|
+
assert_equal "user%3Atest1%40ocelot.cul.columbia.edu", @en.qualified_id_escaped
|
71
|
+
assert_equal "domain:ocelot.cul.columbia.edu", @d_en.qualified_id
|
72
|
+
assert_equal "domain%3Aocelot.cul.columbia.edu", @d_en.qualified_id_escaped
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|