strongmind-platform-sdk 3.9.5 → 3.10.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.
- checksums.yaml +4 -4
- data/Gemfile +3 -0
- data/Gemfile.lock +20 -5
- data/lib/platform_sdk/canvas_api/client.rb +412 -0
- data/lib/platform_sdk/canvas_api.rb +14 -0
- data/lib/platform_sdk/identity/clients.rb +8 -2
- data/lib/platform_sdk/version.rb +1 -1
- data/lib/platform_sdk.rb +2 -0
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf9bfdc1d3b87dbbefbe4b9e1b08d25b544ded0718d31d28d6224e976c4d440c
|
4
|
+
data.tar.gz: eb316b3acc88e86d6e5a326a2a0e8e6c4d50d8ff92cade77f8228b15e80d5940
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ebfdbd2952f4455141f4addd23675245bd048fa9050c01586cd2d6b7ec66ba0348e9f52c9cf0867967712556cfd8b7b0ccc2a2756d02610497f7380206ca3de
|
7
|
+
data.tar.gz: cdc7fa6f4909e56ad84f271dbd8cb8826d941f6ffd51e256a682088400d4fae5f61a7305c6dd45323f1f05b62e69c5c2dca197941da0199e3e192f8bae13f57b
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
strongmind-platform-sdk (3.
|
4
|
+
strongmind-platform-sdk (3.10.1)
|
5
5
|
aws-sdk-secretsmanager (~> 1.66)
|
6
6
|
devise
|
7
7
|
faraday (~> 2.5, >= 2.5.2)
|
8
8
|
faraday-retry
|
9
9
|
jwt
|
10
10
|
learnosity-sdk (~> 0.2.2)
|
11
|
+
multi_json
|
11
12
|
omniauth
|
12
13
|
omniauth-rails_csrf_protection
|
13
14
|
omniauth_openid_connect
|
@@ -38,12 +39,14 @@ GEM
|
|
38
39
|
i18n (>= 1.6, < 2)
|
39
40
|
minitest (>= 5.1)
|
40
41
|
tzinfo (~> 2.0)
|
42
|
+
addressable (2.8.6)
|
43
|
+
public_suffix (>= 2.0.2, < 6.0)
|
41
44
|
aes_key_wrap (1.1.0)
|
42
45
|
ast (2.4.2)
|
43
46
|
attr_required (1.0.2)
|
44
47
|
aws-eventstream (1.3.0)
|
45
|
-
aws-partitions (1.
|
46
|
-
aws-sdk-core (3.191.
|
48
|
+
aws-partitions (1.894.0)
|
49
|
+
aws-sdk-core (3.191.3)
|
47
50
|
aws-eventstream (~> 1, >= 1.3.0)
|
48
51
|
aws-partitions (~> 1, >= 1.651.0)
|
49
52
|
aws-sigv4 (~> 1.8)
|
@@ -55,9 +58,13 @@ GEM
|
|
55
58
|
aws-eventstream (~> 1, >= 1.0.2)
|
56
59
|
base64 (0.2.0)
|
57
60
|
bcrypt (3.1.20)
|
58
|
-
|
61
|
+
bigdecimal (3.1.6)
|
62
|
+
bindata (2.5.0)
|
59
63
|
builder (3.2.4)
|
60
64
|
concurrent-ruby (1.2.2)
|
65
|
+
crack (1.0.0)
|
66
|
+
bigdecimal
|
67
|
+
rexml
|
61
68
|
crass (1.0.6)
|
62
69
|
date (3.3.4)
|
63
70
|
devise (4.9.3)
|
@@ -85,6 +92,7 @@ GEM
|
|
85
92
|
faraday-retry (2.1.0)
|
86
93
|
faraday (~> 2.0)
|
87
94
|
ffi (1.15.5)
|
95
|
+
hashdiff (1.1.0)
|
88
96
|
hashie (5.0.0)
|
89
97
|
i18n (1.13.0)
|
90
98
|
concurrent-ruby (~> 1.0)
|
@@ -112,6 +120,7 @@ GEM
|
|
112
120
|
mini_mime (1.1.5)
|
113
121
|
mini_portile2 (2.8.5)
|
114
122
|
minitest (5.18.0)
|
123
|
+
multi_json (1.15.0)
|
115
124
|
net-imap (0.4.10)
|
116
125
|
date
|
117
126
|
net-protocol
|
@@ -157,7 +166,7 @@ GEM
|
|
157
166
|
ast (~> 2.4.1)
|
158
167
|
public_suffix (5.0.4)
|
159
168
|
racc (1.7.3)
|
160
|
-
rack (2.2.8)
|
169
|
+
rack (2.2.8.1)
|
161
170
|
rack-oauth2 (2.2.1)
|
162
171
|
activesupport
|
163
172
|
attr_required
|
@@ -247,6 +256,10 @@ GEM
|
|
247
256
|
activesupport
|
248
257
|
faraday (~> 2.0)
|
249
258
|
faraday-follow_redirects
|
259
|
+
webmock (3.22.0)
|
260
|
+
addressable (>= 2.8.0)
|
261
|
+
crack (>= 0.3.2)
|
262
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
250
263
|
zeitwerk (2.6.13)
|
251
264
|
|
252
265
|
PLATFORMS
|
@@ -261,12 +274,14 @@ DEPENDENCIES
|
|
261
274
|
faraday-retry
|
262
275
|
jwt (~> 1.5, >= 1.5.4)
|
263
276
|
learnosity-sdk (~> 0.2.2)
|
277
|
+
multi_json
|
264
278
|
rake (~> 13.0)
|
265
279
|
rspec (~> 3.6, >= 3.6.0)
|
266
280
|
rubocop
|
267
281
|
sentry-ruby
|
268
282
|
strongmind-oneroster-client (~> 2.0.3)
|
269
283
|
strongmind-platform-sdk!
|
284
|
+
webmock
|
270
285
|
|
271
286
|
BUNDLED WITH
|
272
287
|
2.3.16
|
@@ -0,0 +1,412 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "faraday"
|
3
|
+
|
4
|
+
module PlatformSdk
|
5
|
+
module CanvasApiWrapper
|
6
|
+
# DataPipeline::Client
|
7
|
+
class Client
|
8
|
+
attr_reader :host, :token, :connection
|
9
|
+
|
10
|
+
PAGE_SIZE = 10
|
11
|
+
|
12
|
+
# @param domain [String]
|
13
|
+
# @param token [String]
|
14
|
+
def initialize(domain:, token:)
|
15
|
+
@host = "https://#{domain}"
|
16
|
+
@token = token
|
17
|
+
@connection = Faraday.new(url: host, headers: { 'Authorization' => "Bearer #{token}" })
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param course_id [Integer]
|
21
|
+
# @returns [Array]
|
22
|
+
def course_modules(course_id:)
|
23
|
+
response = @connection.get("/api/v1/courses/#{course_id}/modules")
|
24
|
+
handle_rate_limiting response
|
25
|
+
if success?(response.status)
|
26
|
+
paginated_items headers: response.headers, initial_response: response.body
|
27
|
+
else
|
28
|
+
result = MultiJson.load response.body, symbolize_keys: true
|
29
|
+
error_messages = ''
|
30
|
+
result[:errors].each do |error|
|
31
|
+
error_messages << "#{error[:message]} "
|
32
|
+
end
|
33
|
+
raise "Error getting course modules: #{error_messages}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param course_id [Integer]
|
38
|
+
# @param module_id [Integer]
|
39
|
+
# @returns [Array]
|
40
|
+
def module_items(course_id:, module_id:)
|
41
|
+
response = @connection.get("/api/v1/courses/#{course_id}/modules/#{module_id}/items?per_page=#{PAGE_SIZE}")
|
42
|
+
handle_rate_limiting response
|
43
|
+
if success?(response.status)
|
44
|
+
paginated_items headers: response.headers, initial_response: response.body
|
45
|
+
else
|
46
|
+
result = MultiJson.load response.body, symbolize_keys: true
|
47
|
+
error_messages = ''
|
48
|
+
result[:errors].each do |error|
|
49
|
+
error_messages << "#{error[:message]} "
|
50
|
+
end
|
51
|
+
raise "Error getting module items: #{error_messages}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# @param course_id [Integer]
|
56
|
+
# @param module_id [Integer]
|
57
|
+
# @param user_id [Integer]
|
58
|
+
# @returns [Array]
|
59
|
+
def user_module_items(course_id:, module_id:, user_id:)
|
60
|
+
uri = "/api/v1/courses/#{course_id}/modules/#{module_id}/items?as_user_id=#{user_id}&per_page=#{PAGE_SIZE}"
|
61
|
+
response = @connection.get(uri)
|
62
|
+
handle_rate_limiting response
|
63
|
+
if success?(response.status)
|
64
|
+
paginated_items headers: response.headers, initial_response: response.body
|
65
|
+
else
|
66
|
+
result = MultiJson.load response.body, symbolize_keys: true
|
67
|
+
error_messages = ''
|
68
|
+
result[:errors].each do |error|
|
69
|
+
error_messages << "#{error[:message]} "
|
70
|
+
end
|
71
|
+
raise "Error getting module items: #{error_messages}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# @param course_id [Integer]
|
76
|
+
# @param module_id [Integer]
|
77
|
+
# @returns [Array]
|
78
|
+
def assignments(course_id:, module_id:)
|
79
|
+
response = @connection.get("/api/v1/courses/#{course_id}/modules/#{module_id}/items?per_page=#{PAGE_SIZE}")
|
80
|
+
handle_rate_limiting response
|
81
|
+
if success?(response.status)
|
82
|
+
assignments = paginated_items headers: response.headers, initial_response: response.body
|
83
|
+
assignments.select { |item| item[:type] == 'Assignment' }
|
84
|
+
else
|
85
|
+
result = MultiJson.load response.body, symbolize_keys: true
|
86
|
+
error_messages = ''
|
87
|
+
result[:errors].each do |error|
|
88
|
+
error_messages << "#{error[:message]} "
|
89
|
+
end
|
90
|
+
raise "Error getting assignments: #{error_messages}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# @param course_id [Integer]
|
95
|
+
def assignment(course_id:, assignment_id:)
|
96
|
+
uri = "api/v1/courses/#{course_id}/assignments/#{assignment_id}"
|
97
|
+
response = @connection.get(uri)
|
98
|
+
handle_rate_limiting response
|
99
|
+
if success?(response.status)
|
100
|
+
MultiJson.load response.body, symbolize_keys: true
|
101
|
+
else
|
102
|
+
result = MultiJson.load response.body, symbolize_keys: true
|
103
|
+
error_messages = ''
|
104
|
+
result[:errors].each do |error|
|
105
|
+
error_messages << "#{error[:message]} "
|
106
|
+
end
|
107
|
+
raise "Error getting quiz submissions#{error_messages}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# @param course_id [Integer]
|
112
|
+
# @param module_id [Integer]
|
113
|
+
# @param module_item_id [Integer]
|
114
|
+
def module_item(course_id:, module_id:, module_item_id:)
|
115
|
+
response = @connection.get("/api/v1/courses/#{course_id}/modules/#{module_id}/items/#{module_item_id}")
|
116
|
+
handle_rate_limiting response
|
117
|
+
if success?(response.status)
|
118
|
+
MultiJson.load response.body, symbolize_keys: true
|
119
|
+
else
|
120
|
+
result = MultiJson.load response.body, symbolize_keys: true
|
121
|
+
error_messages = ''
|
122
|
+
result[:errors].each do |error|
|
123
|
+
error_messages << "#{error[:message]} "
|
124
|
+
end
|
125
|
+
raise "Error getting module item: #{error_messages}"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def user_module_item(course_id:, module_id:, module_item_id:, user_id:)
|
130
|
+
response = @connection.get("/api/v1/courses/#{course_id}/modules/#{module_id}/items/#{module_item_id}?as_user_id=#{user_id}")
|
131
|
+
handle_rate_limiting response
|
132
|
+
if success?(response.status)
|
133
|
+
MultiJson.load response.body, symbolize_keys: true
|
134
|
+
else
|
135
|
+
result = MultiJson.load response.body, symbolize_keys: true
|
136
|
+
error_messages = ''
|
137
|
+
result[:errors].each do |error|
|
138
|
+
error_messages << "#{error[:message]} "
|
139
|
+
end
|
140
|
+
raise "Error getting module item: #{error_messages}"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# @param course_id [Integer]
|
145
|
+
# @param assignment_id [Integer]
|
146
|
+
def submissions(course_id:, assignment_id:)
|
147
|
+
response = @connection.get("/api/v1/courses/#{course_id}/assignments/#{assignment_id}/submissions/")
|
148
|
+
handle_rate_limiting response
|
149
|
+
if success?(response.status)
|
150
|
+
paginated_items headers: response.headers, initial_response: response.body
|
151
|
+
else
|
152
|
+
result = MultiJson.load response.body, symbolize_keys: true
|
153
|
+
error_messages = ''
|
154
|
+
result[:errors].each do |error|
|
155
|
+
error_messages << "#{error[:message]} "
|
156
|
+
end
|
157
|
+
raise "Error getting submissions: #{error_messages}"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def update_submission_excused(course_id:, assignment_id:, user_id:)
|
162
|
+
uri = "/api/v1/courses/#{course_id}/assignments/#{assignment_id}/submissions/#{user_id}"
|
163
|
+
response = @connection.put(uri) do |req|
|
164
|
+
req.headers["Content-Type"] = "application/json"
|
165
|
+
req.body = {
|
166
|
+
submission: {
|
167
|
+
excuse: true
|
168
|
+
}
|
169
|
+
}.to_json
|
170
|
+
end
|
171
|
+
handle_rate_limiting response
|
172
|
+
if success?(response.status)
|
173
|
+
MultiJson.load response.body, symbolize_keys: true
|
174
|
+
else
|
175
|
+
result = MultiJson.load response.body, symbolize_keys: true
|
176
|
+
error_messages = ''
|
177
|
+
result[:errors].each do |error|
|
178
|
+
error_messages << "#{error[:message]} "
|
179
|
+
end
|
180
|
+
raise "Error update submissions to excused: #{error_messages}"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def update_submission_excused_with_comment(course_id:, assignment_id:, user_id:, comment:)
|
185
|
+
uri = "/api/v1/courses/#{course_id}/assignments/#{assignment_id}/submissions/#{user_id}"
|
186
|
+
response = @connection.put(uri) do |req|
|
187
|
+
req.headers['Content-Type'] = 'application/json'
|
188
|
+
req.body = {
|
189
|
+
'comment': {
|
190
|
+
'text_comment': comment
|
191
|
+
},
|
192
|
+
'submission': {
|
193
|
+
'excuse': true
|
194
|
+
}
|
195
|
+
}.to_json
|
196
|
+
end
|
197
|
+
if success?(response.status)
|
198
|
+
MultiJson.load response.body, symbolize_keys: true
|
199
|
+
else
|
200
|
+
result = MultiJson.load response.body, symbolize_keys: true
|
201
|
+
error_messages = ''
|
202
|
+
result[:errors].each do |error|
|
203
|
+
error_messages << "#{error[:message]} "
|
204
|
+
end
|
205
|
+
raise "Error update submissions to excused: #{error_messages}"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# @param course_id [Integer]
|
210
|
+
def course_students(course_id:)
|
211
|
+
uri = "/api/v1/courses/#{course_id}/users?per_page=#{PAGE_SIZE}"
|
212
|
+
params = { 'enrollment_type[]' => 'student' }
|
213
|
+
response = @connection.get(uri, params)
|
214
|
+
handle_rate_limiting response
|
215
|
+
if success?(response.status)
|
216
|
+
paginated_items headers: response.headers, initial_response: response.body
|
217
|
+
else
|
218
|
+
result = MultiJson.load response.body, symbolize_keys: true
|
219
|
+
error_messages = ''
|
220
|
+
result[:errors].each do |error|
|
221
|
+
error_messages << "#{error[:message]} "
|
222
|
+
end
|
223
|
+
raise "Error getting course students: #{error_messages}"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# @param course_id [Integer]
|
228
|
+
# @param quiz_id [Integer]
|
229
|
+
def quiz(course_id:, quiz_id:)
|
230
|
+
uri = "/api/v1/courses/#{course_id}/quizzes/#{quiz_id}"
|
231
|
+
response = @connection.get(uri)
|
232
|
+
handle_rate_limiting response
|
233
|
+
if success?(response.status)
|
234
|
+
MultiJson.load response.body, symbolize_keys: true
|
235
|
+
else
|
236
|
+
result = MultiJson.load response.body, symbolize_keys: true
|
237
|
+
error_messages = ''
|
238
|
+
result[:errors].each do |error|
|
239
|
+
error_messages << "#{error[:message]} "
|
240
|
+
end
|
241
|
+
raise "Error getting quiz: #{error_messages}"
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# @param course_id [Integer]
|
246
|
+
# @param quiz_id [Integer]
|
247
|
+
# @param user_id [Integer]
|
248
|
+
# @return [Array]
|
249
|
+
def quiz_submissions(course_id:, quiz_id:, user_id:)
|
250
|
+
uri = "api/v1/courses/#{course_id}/quizzes/#{quiz_id}/submissions?user=#{user_id}&per_page=#{PAGE_SIZE}"
|
251
|
+
response = @connection.get(uri)
|
252
|
+
handle_rate_limiting response
|
253
|
+
if success?(response.status)
|
254
|
+
paginated_items headers: response.headers, initial_response: response.body
|
255
|
+
else
|
256
|
+
result = MultiJson.load response.body, symbolize_keys: true
|
257
|
+
error_messages = ''
|
258
|
+
result[:errors].each do |error|
|
259
|
+
error_messages << "#{error[:message]} "
|
260
|
+
end
|
261
|
+
raise "Error getting quiz submissions#{error_messages}"
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# @param course_id [Integer]
|
266
|
+
# @param quiz_id [Integer]
|
267
|
+
# @param user_id [Integer]
|
268
|
+
def post_create_quiz_submission(course_id:, quiz_id:, user_id:)
|
269
|
+
uri = "api/v1/courses/#{course_id}/quizzes/#{quiz_id}/submissions?as_user_id=#{user_id}"
|
270
|
+
response = @connection.post(uri)
|
271
|
+
handle_rate_limiting response
|
272
|
+
if success?(response.status)
|
273
|
+
MultiJson.load response.body, symbolize_keys: true
|
274
|
+
else
|
275
|
+
result = MultiJson.load response.body, symbolize_keys: true
|
276
|
+
message = 'Error creating quiz submission'
|
277
|
+
if response.status == 403
|
278
|
+
raise "#{message}: #{result[:message]}"
|
279
|
+
end
|
280
|
+
if result[:errors]
|
281
|
+
raise parse_response_errors message:, result:
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
# @param course_id [Integer]
|
287
|
+
# @param quiz_id [Integer]
|
288
|
+
# @param submission_id [Integer]
|
289
|
+
# @param attempt [Integer]
|
290
|
+
# @param validation_token [String]
|
291
|
+
# @return [Hash]
|
292
|
+
def post_complete_quiz_submission(course_id:, quiz_id:, submission_id:, attempt:, validation_token:)
|
293
|
+
uri = "api/v1/courses/#{course_id}/quizzes/#{quiz_id}/submissions/#{submission_id}/complete"
|
294
|
+
response = @connection.post(uri) do |req|
|
295
|
+
req.headers['Content-Type'] = 'application/json'
|
296
|
+
req.body = { attempt:, validation_token: }.to_json
|
297
|
+
end
|
298
|
+
handle_rate_limiting response
|
299
|
+
if success?(response.status)
|
300
|
+
MultiJson.load response.body, symbolize_keys: true
|
301
|
+
else
|
302
|
+
result = MultiJson.load response.body, symbolize_keys: true
|
303
|
+
message = 'Error completing quiz submission'
|
304
|
+
if response.status == 403
|
305
|
+
raise "#{message}: #{result[:message]}"
|
306
|
+
end
|
307
|
+
if result[:errors]
|
308
|
+
raise parse_response_errors message:, result:
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
# @param course_id [Integer]
|
314
|
+
# @param module_id [Integer]
|
315
|
+
# @param module_item_id [Integer]
|
316
|
+
# @param user_id [Integer]
|
317
|
+
# @return nil
|
318
|
+
def post_mark_module_item_read(course_id:, module_id:, module_item_id:, user_id:)
|
319
|
+
uri = "api/v1/courses/#{course_id}/modules/#{module_id}/items/#{module_item_id}/mark_read?as_user_id=#{user_id}"
|
320
|
+
response = @connection.post(uri)
|
321
|
+
handle_rate_limiting response
|
322
|
+
if success?(response.status)
|
323
|
+
MultiJson.load response.body, symbolize_keys: true
|
324
|
+
else
|
325
|
+
result = MultiJson.load response.body, symbolize_keys: true
|
326
|
+
message = 'Error marking module items read'
|
327
|
+
if response.status == 403
|
328
|
+
raise "#{message}: #{result[:message]}"
|
329
|
+
end
|
330
|
+
if result[:errors]
|
331
|
+
raise parse_response_errors message:, result:
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
def post_discussion_topic_entry(course_id:, topic_id:, user_id:, message:)
|
337
|
+
uri = "api/v1/courses/#{course_id}/discussion_topics/#{topic_id}/entries?as_user_id=#{user_id}"
|
338
|
+
response = @connection.post(uri) do |req|
|
339
|
+
req.headers['Content-Type'] = 'application/json'
|
340
|
+
req.body = { 'message': message.to_s }.to_json
|
341
|
+
end
|
342
|
+
handle_rate_limiting response
|
343
|
+
if success?(response.status)
|
344
|
+
MultiJson.load response.body, symbolize_keys: true
|
345
|
+
else
|
346
|
+
result = MultiJson.load response.body, symbolize_keys: true
|
347
|
+
error_messages = ''
|
348
|
+
result[:errors]&.each do |error|
|
349
|
+
error_messages << "#{error[:message]} "
|
350
|
+
end
|
351
|
+
raise "Error posting a reply: #{error_messages}"
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
private
|
356
|
+
|
357
|
+
# @param headers [Faraday::Utils::Headers]
|
358
|
+
# @return [Array]
|
359
|
+
def paginated_items(headers:, initial_response:)
|
360
|
+
pages = page_links(headers:)
|
361
|
+
items = []
|
362
|
+
items.append MultiJson.load(initial_response, symbolize_keys: true)
|
363
|
+
while pages[:current] != pages[:last]
|
364
|
+
response = @connection.get(pages[:next][:url])
|
365
|
+
handle_rate_limiting response
|
366
|
+
pages = page_links(headers: response.headers)
|
367
|
+
items.append MultiJson.load(response.body, symbolize_keys: true)
|
368
|
+
end
|
369
|
+
items.flatten
|
370
|
+
end
|
371
|
+
|
372
|
+
# @param headers [Faraday::Utils::Headers]
|
373
|
+
# @return [Hash]
|
374
|
+
def page_links(headers:)
|
375
|
+
parts = headers[:link].split(',')
|
376
|
+
parts.map do |part, _|
|
377
|
+
section = part.split(';')
|
378
|
+
name = section[1][/rel="(.*)"/, 1].to_sym
|
379
|
+
url = section[0][/<(.*)>/, 1]
|
380
|
+
[name, { url: }]
|
381
|
+
end.to_h
|
382
|
+
end
|
383
|
+
|
384
|
+
# @param response_code [Integer]
|
385
|
+
# @return [Boolean]
|
386
|
+
def success?(response_code)
|
387
|
+
!!(response_code.to_s =~ /2[0-9]/)
|
388
|
+
end
|
389
|
+
|
390
|
+
# @param message [String]
|
391
|
+
# @param result [Hash]
|
392
|
+
# @return [String]
|
393
|
+
def parse_response_errors(message:, result:)
|
394
|
+
error_messages = ''
|
395
|
+
result[:errors].each do |error|
|
396
|
+
error_messages << "#{error[:message]} "
|
397
|
+
end
|
398
|
+
"#{message}: {#{error_messages}"
|
399
|
+
end
|
400
|
+
|
401
|
+
def handle_rate_limiting(response)
|
402
|
+
remaining_requests = response.headers["X-Rate-Limit-Remaining"].to_i
|
403
|
+
reset_time = response.headers["X-Rate-Limit-Reset"].to_i
|
404
|
+
|
405
|
+
return unless remaining_requests.zero?
|
406
|
+
|
407
|
+
sleep_time = [reset_time - Time.now.to_i, 0].max + 1
|
408
|
+
sleep(sleep_time)
|
409
|
+
end
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "faraday"
|
4
|
+
require "faraday/net_http"
|
5
|
+
require "faraday/retry"
|
6
|
+
require "platform_sdk/canvas_api/client"
|
7
|
+
|
8
|
+
module PlatformSdk
|
9
|
+
module CanvasApiWrapper
|
10
|
+
class Error < StandardError; end
|
11
|
+
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -65,10 +65,16 @@ module PlatformSdk
|
|
65
65
|
Time.at(JWT.decode(jwt, nil, false)[0]["exp"])
|
66
66
|
end
|
67
67
|
|
68
|
-
def
|
68
|
+
def refresh_token_if_expired(jwt:, refresh_token:)
|
69
69
|
raise ArgumentError if refresh_token.nil? || jwt.nil?
|
70
70
|
return unless token_expired?(jwt)
|
71
71
|
|
72
|
+
refresh_token(refresh_token)
|
73
|
+
end
|
74
|
+
|
75
|
+
def refresh_token(refresh_token)
|
76
|
+
raise ArgumentError if refresh_token.nil?
|
77
|
+
|
72
78
|
response = @conn.post("/connect/token", request_body(
|
73
79
|
grant_type: "refresh_token", refresh_token:))
|
74
80
|
response.body.transform_keys!(&:to_sym)
|
@@ -77,7 +83,7 @@ module PlatformSdk
|
|
77
83
|
def refresh_session(session: {})
|
78
84
|
raise ArgumentError if session[:access_token].nil? || session[:refresh_token].nil?
|
79
85
|
|
80
|
-
refreshed_tokens =
|
86
|
+
refreshed_tokens = refresh_token_if_expired(jwt: session[:access_token], refresh_token: session[:refresh_token])
|
81
87
|
|
82
88
|
return if refreshed_tokens.nil?
|
83
89
|
|
data/lib/platform_sdk/version.rb
CHANGED
data/lib/platform_sdk.rb
CHANGED
@@ -11,6 +11,8 @@ require "platform_sdk/data_pipeline"
|
|
11
11
|
require "platform_sdk/pencil_spaces"
|
12
12
|
require "platform_sdk/edfi"
|
13
13
|
require "platform_sdk/sentry"
|
14
|
+
require "platform_sdk/learnosity_api"
|
15
|
+
require "platform_sdk/canvas_api"
|
14
16
|
|
15
17
|
module PlatformSdk
|
16
18
|
class Error < StandardError; end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: strongmind-platform-sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.10.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Platform Team
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-02-
|
11
|
+
date: 2024-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -184,6 +184,20 @@ dependencies:
|
|
184
184
|
- - ">="
|
185
185
|
- !ruby/object:Gem::Version
|
186
186
|
version: '0'
|
187
|
+
- !ruby/object:Gem::Dependency
|
188
|
+
name: multi_json
|
189
|
+
requirement: !ruby/object:Gem::Requirement
|
190
|
+
requirements:
|
191
|
+
- - ">="
|
192
|
+
- !ruby/object:Gem::Version
|
193
|
+
version: '0'
|
194
|
+
type: :runtime
|
195
|
+
prerelease: false
|
196
|
+
version_requirements: !ruby/object:Gem::Requirement
|
197
|
+
requirements:
|
198
|
+
- - ">="
|
199
|
+
- !ruby/object:Gem::Version
|
200
|
+
version: '0'
|
187
201
|
description: Includes a wrapper for the swagger generated OneRoster management clients
|
188
202
|
email:
|
189
203
|
- horseshoes@strongmind.com
|
@@ -214,6 +228,8 @@ files:
|
|
214
228
|
- lib/platform_sdk.rb
|
215
229
|
- lib/platform_sdk/aws.rb
|
216
230
|
- lib/platform_sdk/aws/secrets_manager_client.rb
|
231
|
+
- lib/platform_sdk/canvas_api.rb
|
232
|
+
- lib/platform_sdk/canvas_api/client.rb
|
217
233
|
- lib/platform_sdk/data_pipeline.rb
|
218
234
|
- lib/platform_sdk/data_pipeline/data_pipeline_client.rb
|
219
235
|
- lib/platform_sdk/edfi.rb
|