openstax_accounts 4.1.1 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -1
- data/app/controllers/openstax/accounts/application_controller.rb +1 -1
- data/app/models/openstax/accounts/account.rb +11 -6
- data/app/models/openstax/accounts/anonymous_account.rb +1 -1
- data/app/models/openstax/accounts/application_account.rb +2 -1
- data/app/models/openstax/accounts/group.rb +14 -12
- data/app/models/openstax/accounts/group_member.rb +10 -10
- data/app/models/openstax/accounts/group_nesting.rb +7 -13
- data/app/models/openstax/accounts/group_owner.rb +10 -10
- data/app/representers/openstax/accounts/api/v1/account_representer.rb +4 -3
- data/app/representers/openstax/accounts/api/v1/application_account_representer.rb +7 -2
- data/app/representers/openstax/accounts/api/v1/application_accounts_representer.rb +0 -2
- data/app/representers/openstax/accounts/api/v1/application_group_representer.rb +7 -2
- data/app/representers/openstax/accounts/api/v1/application_groups_representer.rb +0 -2
- data/app/representers/openstax/accounts/api/v1/group_nesting_representer.rb +1 -1
- data/app/representers/openstax/accounts/api/v1/group_representer.rb +1 -1
- data/app/representers/openstax/accounts/api/v1/group_user_representer.rb +1 -1
- data/app/routines/openstax/accounts/search_accounts.rb +1 -1
- data/app/routines/openstax/accounts/sync_accounts.rb +24 -27
- data/app/routines/openstax/accounts/sync_groups.rb +22 -26
- data/lib/openstax/accounts/action_controller/base.rb +61 -0
- data/lib/openstax/accounts/api.rb +270 -0
- data/lib/openstax/accounts/configuration.rb +77 -0
- data/lib/openstax/accounts/engine.rb +13 -11
- data/lib/openstax/accounts/has_many_through_groups/active_record/base.rb +51 -0
- data/lib/openstax/accounts/version.rb +1 -1
- data/lib/openstax_accounts.rb +8 -331
- data/spec/dummy/app/controllers/api/users_controller.rb +4 -0
- data/spec/dummy/config/routes.rb +3 -1
- data/spec/dummy/log/test.log +152307 -0
- data/spec/lib/openstax/accounts/api_spec.rb +232 -0
- data/spec/lib/openstax/accounts/has_many_through_groups/active_record/base_spec.rb +57 -0
- data/spec/routines/openstax/accounts/sync_accounts_spec.rb +5 -9
- data/spec/routines/openstax/accounts/sync_groups_spec.rb +38 -39
- metadata +27 -16
- data/lib/openstax/accounts/extend_builtins.rb +0 -50
- data/lib/openstax/accounts/has_many_through_groups.rb +0 -47
- data/spec/lib/openstax/accounts/has_many_through_groups_spec.rb +0 -53
- data/spec/lib/openstax_accounts_spec.rb +0 -210
@@ -1,18 +1,20 @@
|
|
1
|
-
require 'omniauth'
|
2
|
-
require 'omniauth/strategies/openstax'
|
3
|
-
require 'lev'
|
4
|
-
require 'roar/decorator'
|
5
|
-
require 'roar/representer/json'
|
6
|
-
require 'keyword_search'
|
7
|
-
require 'squeel'
|
8
|
-
require 'action_interceptor'
|
9
|
-
require 'openstax/accounts/extend_builtins'
|
10
|
-
require 'doorkeeper'
|
11
|
-
|
12
1
|
ActiveSupport::Inflector.inflections do |inflect|
|
13
2
|
inflect.acronym 'OpenStax'
|
14
3
|
end
|
15
4
|
|
5
|
+
require 'action_interceptor'
|
6
|
+
require 'doorkeeper'
|
7
|
+
require 'keyword_search'
|
8
|
+
require 'lev'
|
9
|
+
require 'representable'
|
10
|
+
require 'representable/json/collection'
|
11
|
+
require 'roar'
|
12
|
+
require 'roar/decorator'
|
13
|
+
require 'roar/json'
|
14
|
+
require 'squeel'
|
15
|
+
require 'openstax/accounts/action_controller/base'
|
16
|
+
require 'openstax/accounts/has_many_through_groups/active_record/base'
|
17
|
+
|
16
18
|
module OpenStax
|
17
19
|
module Accounts
|
18
20
|
class Engine < ::Rails::Engine
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module OpenStax
|
2
|
+
module Accounts
|
3
|
+
module HasManyThroughGroups
|
4
|
+
module ActiveRecord
|
5
|
+
module Base
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def has_many_through_groups(groups_name, name, options = {})
|
12
|
+
options = {class_name: name.to_s.classify}.merge(options)
|
13
|
+
association_name = "direct_#{name.to_s}".to_sym
|
14
|
+
|
15
|
+
OpenStax::Accounts::Group.class_exec do
|
16
|
+
has_many association_name, options
|
17
|
+
|
18
|
+
define_method(name) do
|
19
|
+
OpenStax::Accounts::Group.includes(association_name)
|
20
|
+
.where(openstax_uid: supertree_group_ids)
|
21
|
+
.collect{|g| g.send(association_name).to_a}.flatten.uniq
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class_exec do
|
26
|
+
has_many association_name, options if options[:as]
|
27
|
+
|
28
|
+
define_method(name) do
|
29
|
+
direct_records = respond_to?(association_name) ? \
|
30
|
+
send(association_name).to_a : []
|
31
|
+
indirect_records = OpenStax::Accounts::Group
|
32
|
+
.includes(association_name).where(
|
33
|
+
openstax_uid: send(groups_name).collect{|g|
|
34
|
+
g.supertree_group_ids
|
35
|
+
}.flatten.uniq
|
36
|
+
)
|
37
|
+
.collect{|g| g.send(association_name).to_a}
|
38
|
+
(direct_records + indirect_records).flatten.uniq
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
::ActiveRecord::Base.send(
|
50
|
+
:include, OpenStax::Accounts::HasManyThroughGroups::ActiveRecord::Base
|
51
|
+
)
|
data/lib/openstax_accounts.rb
CHANGED
@@ -1,22 +1,19 @@
|
|
1
|
-
require 'openstax/accounts/version'
|
2
|
-
require 'openstax/accounts/engine'
|
3
|
-
require 'openstax/accounts/default_account_user_mapper'
|
4
|
-
require 'openstax/accounts/current_user_manager'
|
5
|
-
require 'openstax/accounts/has_many_through_groups'
|
6
|
-
require 'openstax_utilities'
|
7
|
-
|
8
1
|
require 'oauth2'
|
2
|
+
require 'omniauth'
|
3
|
+
require 'openstax_utilities'
|
9
4
|
require 'uri'
|
5
|
+
require 'omniauth/strategies/openstax'
|
6
|
+
require 'openstax/accounts/api'
|
7
|
+
require 'openstax/accounts/configuration'
|
8
|
+
require 'openstax/accounts/current_user_manager'
|
9
|
+
require 'openstax/accounts/default_account_user_mapper'
|
10
|
+
require 'openstax/accounts/engine'
|
10
11
|
|
11
12
|
module OpenStax
|
12
13
|
module Accounts
|
13
14
|
|
14
|
-
DEFAULT_API_VERSION = :v1
|
15
|
-
|
16
15
|
class << self
|
17
16
|
|
18
|
-
mattr_accessor :syncing
|
19
|
-
|
20
17
|
###########################################################################
|
21
18
|
#
|
22
19
|
# Configuration machinery.
|
@@ -39,326 +36,6 @@ module OpenStax
|
|
39
36
|
@configuration ||= Configuration.new
|
40
37
|
end
|
41
38
|
|
42
|
-
class Configuration
|
43
|
-
# openstax_accounts_url
|
44
|
-
# Base URL for OpenStax Accounts
|
45
|
-
attr_reader :openstax_accounts_url
|
46
|
-
|
47
|
-
# openstax_application_id
|
48
|
-
# OAuth client_id received from OpenStax Accounts
|
49
|
-
attr_accessor :openstax_application_id
|
50
|
-
|
51
|
-
# openstax_application_secret
|
52
|
-
# OAuth client_secret received from OpenStax Accounts
|
53
|
-
attr_accessor :openstax_application_secret
|
54
|
-
|
55
|
-
# enable_stubbing
|
56
|
-
# Set to true if you want this engine to fake all
|
57
|
-
# interaction with the accounts site.
|
58
|
-
attr_accessor :enable_stubbing
|
59
|
-
|
60
|
-
# logout_via
|
61
|
-
# HTTP method to accept for logout requests
|
62
|
-
attr_accessor :logout_via
|
63
|
-
|
64
|
-
attr_accessor :default_errors_partial
|
65
|
-
attr_accessor :default_errors_html_id
|
66
|
-
attr_accessor :default_errors_added_trigger
|
67
|
-
|
68
|
-
# security_transgression_exception
|
69
|
-
# Class to be used for security transgression exceptions
|
70
|
-
attr_accessor :security_transgression_exception
|
71
|
-
|
72
|
-
# account_user_mapper
|
73
|
-
# This class teaches the gem how to convert between accounts and users
|
74
|
-
# See the "account_user_mapper" discussion in the README
|
75
|
-
attr_accessor :account_user_mapper
|
76
|
-
|
77
|
-
# min_search_characters
|
78
|
-
# The minimum number of characters that can be used
|
79
|
-
# as a query in a call to the AccountsSearch handler
|
80
|
-
# If less are used, the handler will return an error instead
|
81
|
-
attr_accessor :min_search_characters
|
82
|
-
|
83
|
-
# max_search_items
|
84
|
-
# The maximum number of accounts that can be returned
|
85
|
-
# in a call to the AccountsSearch handler
|
86
|
-
# If more would be returned, the result will be empty instead
|
87
|
-
attr_accessor :max_search_items
|
88
|
-
|
89
|
-
def openstax_accounts_url=(url)
|
90
|
-
url.gsub!(/https|http/,'https') if !(url =~ /localhost/)
|
91
|
-
url = url + "/" if url[url.size-1] != '/'
|
92
|
-
@openstax_accounts_url = url
|
93
|
-
end
|
94
|
-
|
95
|
-
def initialize
|
96
|
-
@openstax_application_id = 'SET ME!'
|
97
|
-
@openstax_application_secret = 'SET ME!'
|
98
|
-
@openstax_accounts_url = 'https://accounts.openstax.org/'
|
99
|
-
@enable_stubbing = true
|
100
|
-
@logout_via = :get
|
101
|
-
@default_errors_partial = 'openstax/accounts/shared/attention'
|
102
|
-
@default_errors_html_id = 'openstax-accounts-attention'
|
103
|
-
@default_errors_added_trigger = 'openstax-accounts-errors-added'
|
104
|
-
@security_transgression_exception = SecurityTransgression
|
105
|
-
@account_user_mapper = OpenStax::Accounts::DefaultAccountUserMapper
|
106
|
-
@min_search_characters = 3
|
107
|
-
@max_search_items = 10
|
108
|
-
super
|
109
|
-
end
|
110
|
-
|
111
|
-
def enable_stubbing?
|
112
|
-
!Rails.env.production? && enable_stubbing
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
# Executes an OpenStax Accounts API call, using the given HTTP method,
|
117
|
-
# API url and request options.
|
118
|
-
# Any options accepted by OAuth2 requests can be used, such as
|
119
|
-
# :params, :body, :headers, etc, plus the :access_token option, which can
|
120
|
-
# be used to manually specify an OAuth access token.
|
121
|
-
# On failure, it can throw Faraday::ConnectionFailed for connection errors
|
122
|
-
# or OAuth2::Error if Accounts returns an HTTP 400 error,
|
123
|
-
# such as 422 Unprocessable Entity.
|
124
|
-
# On success, returns an OAuth2::Response object.
|
125
|
-
def api_call(http_method, url, options = {})
|
126
|
-
version = options.delete(:api_version)
|
127
|
-
unless version.blank?
|
128
|
-
options[:headers] ||= {}
|
129
|
-
options[:headers].merge!({
|
130
|
-
'Accept' => "application/vnd.accounts.openstax.#{version.to_s}"
|
131
|
-
})
|
132
|
-
end
|
133
|
-
|
134
|
-
token_string = options.delete(:access_token)
|
135
|
-
token = token_string.blank? ? client.client_credentials.get_token :
|
136
|
-
OAuth2::AccessToken.new(client, token_string)
|
137
|
-
|
138
|
-
api_url = URI.join(configuration.openstax_accounts_url, 'api/', url)
|
139
|
-
|
140
|
-
token.request(http_method, api_url, options)
|
141
|
-
end
|
142
|
-
|
143
|
-
# Performs an account search in the Accounts server.
|
144
|
-
# Results are limited to 10 accounts maximum.
|
145
|
-
# Takes a query parameter and an optional API version parameter.
|
146
|
-
# API version currently defaults to :v1 (may change in the future).
|
147
|
-
# On failure, throws an Exception, just like api_call.
|
148
|
-
# On success, returns an OAuth2::Response object.
|
149
|
-
def search_accounts(query, version = DEFAULT_API_VERSION)
|
150
|
-
options = {:params => {:q => query},
|
151
|
-
:api_version => version}
|
152
|
-
api_call(:get, 'users', options)
|
153
|
-
end
|
154
|
-
|
155
|
-
# Updates a user account in the Accounts server.
|
156
|
-
# The account is determined by the OAuth access token.
|
157
|
-
# Also takes an optional API version parameter.
|
158
|
-
# API version currently defaults to :v1 (may change in the future).
|
159
|
-
# On failure, throws an Exception, just like api_call.
|
160
|
-
# On success, returns an OAuth2::Response object.
|
161
|
-
def update_account(account, version = DEFAULT_API_VERSION)
|
162
|
-
options = {:access_token => account.access_token,
|
163
|
-
:api_version => version,
|
164
|
-
:body => account.attributes.slice('username', 'first_name',
|
165
|
-
'last_name', 'full_name', 'title').to_json}
|
166
|
-
api_call(:put, 'user', options)
|
167
|
-
end
|
168
|
-
|
169
|
-
# Performs an account search in the Accounts server.
|
170
|
-
# Results are limited to accounts that have used the current app.
|
171
|
-
# Takes a query parameter and an optional API version parameter.
|
172
|
-
# API version currently defaults to :v1 (may change in the future).
|
173
|
-
# On failure, throws an Exception, just like api_call.
|
174
|
-
# On success, returns an OAuth2::Response object.
|
175
|
-
def search_application_accounts(query, version = DEFAULT_API_VERSION)
|
176
|
-
options = {:params => {:q => query},
|
177
|
-
:api_version => version}
|
178
|
-
api_call(:get, 'application_users', options)
|
179
|
-
end
|
180
|
-
|
181
|
-
# Retrieves information about accounts that have been
|
182
|
-
# recently updated.
|
183
|
-
# Results are limited to accounts that have used the current app.
|
184
|
-
# On failure, throws an Exception, just like api_call.
|
185
|
-
# On success, returns an OAuth2::Response object.
|
186
|
-
def get_application_account_updates(version = DEFAULT_API_VERSION)
|
187
|
-
options = {:api_version => version}
|
188
|
-
api_call(:get, 'application_users/updates', options)
|
189
|
-
end
|
190
|
-
|
191
|
-
# Marks account updates as "read".
|
192
|
-
# The application_users parameter is an array of hashes.
|
193
|
-
# Each hash has 2 required fields: 'id', which should contain the
|
194
|
-
# application_user's id, and 'read_updates', which should contain
|
195
|
-
# the last received value of unread_updates for that application_user.
|
196
|
-
# Can only be called for application_users that belong to the current app.
|
197
|
-
# On failure, throws an Exception, just like api_call.
|
198
|
-
# On success, returns an OAuth2::Response object.
|
199
|
-
def mark_account_updates_as_read(application_users, version = DEFAULT_API_VERSION)
|
200
|
-
options = {:api_version => version,
|
201
|
-
:body => application_users.to_json}
|
202
|
-
api_call(:put, 'application_users/updated', options)
|
203
|
-
end
|
204
|
-
|
205
|
-
# Retrieves information about groups that have been
|
206
|
-
# recently updated.
|
207
|
-
# Results are limited to groups that users of the current app have access to.
|
208
|
-
# On failure, throws an Exception, just like api_call.
|
209
|
-
# On success, returns an OAuth2::Response object.
|
210
|
-
def get_application_group_updates(version = DEFAULT_API_VERSION)
|
211
|
-
options = {:api_version => version}
|
212
|
-
api_call(:get, 'application_groups/updates', options)
|
213
|
-
end
|
214
|
-
|
215
|
-
# Marks group updates as "read".
|
216
|
-
# The application_groups parameter is an array of hashes.
|
217
|
-
# Each hash has 2 required fields: 'id', which should contain the
|
218
|
-
# application_group's id, and 'read_updates', which should contain
|
219
|
-
# the last received value of unread_updates for that application_group.
|
220
|
-
# Can only be called for application_groups that belong to the current app.
|
221
|
-
# On failure, throws an Exception, just like api_call.
|
222
|
-
# On success, returns an OAuth2::Response object.
|
223
|
-
def mark_group_updates_as_read(application_groups, version = DEFAULT_API_VERSION)
|
224
|
-
options = {:api_version => version,
|
225
|
-
:body => application_groups.to_json}
|
226
|
-
api_call(:put, 'application_groups/updated', options)
|
227
|
-
end
|
228
|
-
|
229
|
-
# Creates a group in the Accounts server.
|
230
|
-
# The given account will be the owner of the group.
|
231
|
-
# Also takes an optional API version parameter.
|
232
|
-
# API version currently defaults to :v1 (may change in the future).
|
233
|
-
# On failure, throws an Exception, just like api_call.
|
234
|
-
# On success, returns an OAuth2::Response object.
|
235
|
-
def create_group(account, group, version = DEFAULT_API_VERSION)
|
236
|
-
options = {:access_token => account.access_token,
|
237
|
-
:api_version => version,
|
238
|
-
:body => group.attributes.slice('name', 'is_public').to_json}
|
239
|
-
response = ActiveSupport::JSON.decode(api_call(
|
240
|
-
:post, 'groups', options).body)
|
241
|
-
group.openstax_uid = response['id']
|
242
|
-
response
|
243
|
-
end
|
244
|
-
|
245
|
-
# Updates a group in the Accounts server.
|
246
|
-
# The given account must own the group.
|
247
|
-
# Also takes an optional API version parameter.
|
248
|
-
# API version currently defaults to :v1 (may change in the future).
|
249
|
-
# On failure, throws an Exception, just like api_call.
|
250
|
-
# On success, returns an OAuth2::Response object.
|
251
|
-
def update_group(account, group, version = DEFAULT_API_VERSION)
|
252
|
-
options = {:access_token => account.access_token,
|
253
|
-
:api_version => version,
|
254
|
-
:body => group.attributes.slice('name', 'is_public').to_json}
|
255
|
-
api_call(:put, "groups/#{group.openstax_uid}", options)
|
256
|
-
end
|
257
|
-
|
258
|
-
# Deletes a group from the Accounts server.
|
259
|
-
# The given account must own the group.
|
260
|
-
# Also takes an optional API version parameter.
|
261
|
-
# API version currently defaults to :v1 (may change in the future).
|
262
|
-
# On failure, throws an Exception, just like api_call.
|
263
|
-
# On success, returns an OAuth2::Response object.
|
264
|
-
def destroy_group(account, group, version = DEFAULT_API_VERSION)
|
265
|
-
options = {:access_token => account.access_token,
|
266
|
-
:api_version => version}
|
267
|
-
api_call(:delete, "groups/#{group.openstax_uid}", options)
|
268
|
-
end
|
269
|
-
|
270
|
-
# Creates a group_member in the Accounts server.
|
271
|
-
# The given account must own the group.
|
272
|
-
# Also takes an optional API version parameter.
|
273
|
-
# API version currently defaults to :v1 (may change in the future).
|
274
|
-
# On failure, throws an Exception, just like api_call.
|
275
|
-
# On success, returns an OAuth2::Response object.
|
276
|
-
def create_group_member(account, group_member, version = DEFAULT_API_VERSION)
|
277
|
-
options = {:access_token => account.access_token,
|
278
|
-
:api_version => version}
|
279
|
-
api_call(:post,
|
280
|
-
"groups/#{group_member.group_id}/members/#{group_member.user_id}",
|
281
|
-
options)
|
282
|
-
end
|
283
|
-
|
284
|
-
# Deletes a group_member from the Accounts server.
|
285
|
-
# The given account must own the group.
|
286
|
-
# Also takes an optional API version parameter.
|
287
|
-
# API version currently defaults to :v1 (may change in the future).
|
288
|
-
# On failure, throws an Exception, just like api_call.
|
289
|
-
# On success, returns an OAuth2::Response object.
|
290
|
-
def destroy_group_member(account, group_member, version = DEFAULT_API_VERSION)
|
291
|
-
options = {:access_token => account.access_token,
|
292
|
-
:api_version => version}
|
293
|
-
api_call(:delete,
|
294
|
-
"groups/#{group_member.group_id}/members/#{group_member.user_id}",
|
295
|
-
options)
|
296
|
-
end
|
297
|
-
|
298
|
-
# Creates a group_owner in the Accounts server.
|
299
|
-
# The given account must own the group.
|
300
|
-
# Also takes an optional API version parameter.
|
301
|
-
# API version currently defaults to :v1 (may change in the future).
|
302
|
-
# On failure, throws an Exception, just like api_call.
|
303
|
-
# On success, returns an OAuth2::Response object.
|
304
|
-
def create_group_owner(account, group_owner, version = DEFAULT_API_VERSION)
|
305
|
-
options = {:access_token => account.access_token,
|
306
|
-
:api_version => version}
|
307
|
-
api_call(:post,
|
308
|
-
"groups/#{group_owner.group_id}/owners/#{group_owner.user_id}",
|
309
|
-
options)
|
310
|
-
end
|
311
|
-
|
312
|
-
# Deletes a group_owner from the Accounts server.
|
313
|
-
# The given account must own the group.
|
314
|
-
# Also takes an optional API version parameter.
|
315
|
-
# API version currently defaults to :v1 (may change in the future).
|
316
|
-
# On failure, throws an Exception, just like api_call.
|
317
|
-
# On success, returns an OAuth2::Response object.
|
318
|
-
def destroy_group_owner(account, group_owner, version = DEFAULT_API_VERSION)
|
319
|
-
options = {:access_token => account.access_token,
|
320
|
-
:api_version => version}
|
321
|
-
api_call(:delete,
|
322
|
-
"groups/#{group_owner.group_id}/owners/#{group_owner.user_id}",
|
323
|
-
options)
|
324
|
-
end
|
325
|
-
|
326
|
-
# Creates a group_nesting in the Accounts server.
|
327
|
-
# The given account must own both groups.
|
328
|
-
# Also takes an optional API version parameter.
|
329
|
-
# API version currently defaults to :v1 (may change in the future).
|
330
|
-
# On failure, throws an Exception, just like api_call.
|
331
|
-
# On success, returns an OAuth2::Response object.
|
332
|
-
def create_group_nesting(account, group_nesting, version = DEFAULT_API_VERSION)
|
333
|
-
options = {:access_token => account.access_token,
|
334
|
-
:api_version => version}
|
335
|
-
api_call(:post,
|
336
|
-
"groups/#{group_nesting.container_group_id}/nestings/#{group_nesting.member_group_id}",
|
337
|
-
options)
|
338
|
-
end
|
339
|
-
|
340
|
-
# Deletes a group_nesting from the Accounts server.
|
341
|
-
# The given account must own either group.
|
342
|
-
# Also takes an optional API version parameter.
|
343
|
-
# API version currently defaults to :v1 (may change in the future).
|
344
|
-
# On failure, throws an Exception, just like api_call.
|
345
|
-
# On success, returns an OAuth2::Response object.
|
346
|
-
def destroy_group_nesting(account, group_nesting, version = DEFAULT_API_VERSION)
|
347
|
-
options = {:access_token => account.access_token,
|
348
|
-
:api_version => version}
|
349
|
-
api_call(:delete,
|
350
|
-
"groups/#{group_nesting.container_group_id}/nestings/#{group_nesting.member_group_id}",
|
351
|
-
options)
|
352
|
-
end
|
353
|
-
|
354
|
-
protected
|
355
|
-
|
356
|
-
def client
|
357
|
-
@client ||= OAuth2::Client.new(configuration.openstax_application_id,
|
358
|
-
configuration.openstax_application_secret,
|
359
|
-
:site => configuration.openstax_accounts_url)
|
360
|
-
end
|
361
|
-
|
362
39
|
end
|
363
40
|
|
364
41
|
end
|