atomic_lti 1.5.7 → 1.6.0
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/README.md +2 -0
- data/app/assets/builds/atomic_lti/init_app.js +17 -17
- data/app/assets/builds/atomic_lti/init_app.js.map +4 -4
- data/app/lib/atomic_lti/exceptions.rb +3 -0
- data/app/lib/atomic_lti/paging_helper.rb +36 -0
- data/app/lib/atomic_lti/services/base.rb +48 -5
- data/app/lib/atomic_lti/services/line_items.rb +26 -6
- data/app/lib/atomic_lti/services/names_and_roles.rb +21 -8
- data/app/lib/atomic_lti/services/results.rb +22 -6
- data/lib/atomic_lti/open_id_middleware.rb +2 -2
- data/lib/atomic_lti/version.rb +1 -1
- metadata +4 -3
@@ -0,0 +1,36 @@
|
|
1
|
+
module AtomicLti
|
2
|
+
module PagingHelper
|
3
|
+
MAX_PAGES = 200
|
4
|
+
|
5
|
+
def self.response_link_urls(response, *rels)
|
6
|
+
links = response.headers["link"]&.split(",") || []
|
7
|
+
urls = {}
|
8
|
+
rels.each do |rel|
|
9
|
+
urls[rel] = link_url(links, rel)
|
10
|
+
end
|
11
|
+
urls.values_at(*rels)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.link_url(links, rel)
|
15
|
+
matching_link = links.detect { |link| link.include?("rel=\"#{rel}\"") }
|
16
|
+
|
17
|
+
return unless matching_link
|
18
|
+
|
19
|
+
matching_link.split(";")[0].gsub(/[<>\s]/, "")
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.paginate_request
|
23
|
+
all = []
|
24
|
+
next_link = nil
|
25
|
+
loop do
|
26
|
+
result, next_link = yield(next_link)
|
27
|
+
all << result
|
28
|
+
|
29
|
+
break if next_link.blank? || result.blank?
|
30
|
+
|
31
|
+
raise AtomicLti::Exceptions::PaginationLimitExceeded if all.count > MAX_PAGES
|
32
|
+
end
|
33
|
+
all.compact.flatten
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -26,14 +26,57 @@ module AtomicLti
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def get_next_url(response)
|
29
|
-
|
30
|
-
|
29
|
+
next_url, = AtomicLti::PagingHelper.response_link_urls(response, "next")
|
30
|
+
next_url
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
def service_get(*args)
|
34
|
+
logged_service_call(:get, *args)
|
35
|
+
end
|
36
|
+
|
37
|
+
def service_put(*args)
|
38
|
+
logged_service_call(:put, *args)
|
39
|
+
end
|
40
|
+
|
41
|
+
def service_post(*args)
|
42
|
+
logged_service_call(:post, *args)
|
43
|
+
end
|
44
|
+
|
45
|
+
def service_delete(*args)
|
46
|
+
logged_service_call(:delete, *args)
|
35
47
|
end
|
36
48
|
|
49
|
+
def logged_service_call(method, *args)
|
50
|
+
Rails.logger.debug("Making service call #{method} #{args}")
|
51
|
+
response = HTTParty.send(method, *args)
|
52
|
+
Rails.logger.debug("Got status #{response.code} for service call #{method} #{args}")
|
53
|
+
|
54
|
+
if response.body.present? && response.success?
|
55
|
+
parsed_body = JSON.parse(response.body)
|
56
|
+
end
|
57
|
+
|
58
|
+
if !response.success? && response.code != 404
|
59
|
+
Rails.logger.error("Encountered an error while making service request #{method} #{args}")
|
60
|
+
Rails.logger.error("Got code #{response.code}")
|
61
|
+
Rails.logger.error(response.body)
|
62
|
+
end
|
63
|
+
|
64
|
+
[response, parsed_body]
|
65
|
+
rescue JSON::ParserError => e
|
66
|
+
# We do not reraise this error as previously we did not check at all for valid json. This is purely for
|
67
|
+
# logging purposes.
|
68
|
+
Rails.logger.error("Encountered an error while parsing response for service request #{method} #{args}")
|
69
|
+
Rails.logger.error(response.body)
|
70
|
+
Rails.logger.error(e)
|
71
|
+
|
72
|
+
[response, nil]
|
73
|
+
rescue StandardError => e
|
74
|
+
Rails.logger.error("Encountered an error while making service request #{method} #{args}")
|
75
|
+
Rails.logger.error(response&.body)
|
76
|
+
Rails.logger.error(e)
|
77
|
+
|
78
|
+
raise e
|
79
|
+
end
|
37
80
|
end
|
38
81
|
end
|
39
82
|
end
|
@@ -60,16 +60,33 @@ module AtomicLti
|
|
60
60
|
|
61
61
|
# List line items
|
62
62
|
# Canvas: https://canvas.beta.instructure.com/doc/api/line_items.html#method.lti/ims/line_items.index
|
63
|
-
def list(query = {})
|
63
|
+
def list(query = {}, page_url: nil)
|
64
|
+
url = if page_url.present?
|
65
|
+
page_url
|
66
|
+
else
|
67
|
+
uri = Addressable::URI.parse(endpoint(@id_token_decoded))
|
68
|
+
uri.query_values = (uri.query_values || {}).merge(query)
|
69
|
+
uri.to_str
|
70
|
+
end
|
71
|
+
|
64
72
|
accept = { "Accept" => "application/vnd.ims.lis.v2.lineitemcontainer+json" }
|
65
|
-
|
73
|
+
response, = service_get(url, headers: headers(accept))
|
74
|
+
response
|
75
|
+
end
|
76
|
+
|
77
|
+
def list_all(query = {})
|
78
|
+
AtomicLti::PagingHelper.paginate_request do |next_link|
|
79
|
+
result_page = list(query, page_url: next_link)
|
80
|
+
[JSON.parse(result_page.body), get_next_url(result_page)]
|
81
|
+
end
|
66
82
|
end
|
67
83
|
|
68
84
|
# Get a specific line item
|
69
85
|
# https://canvas.beta.instructure.com/doc/api/line_items.html#method.lti/ims/line_items.show
|
70
86
|
def show(line_item_url)
|
71
87
|
accept = { "Accept" => "application/vnd.ims.lis.v2.lineitem+json" }
|
72
|
-
|
88
|
+
response, = service_get(line_item_url, headers: headers(accept))
|
89
|
+
response
|
73
90
|
end
|
74
91
|
|
75
92
|
# Create a line item
|
@@ -77,18 +94,21 @@ module AtomicLti
|
|
77
94
|
# Canvas: https://canvas.beta.instructure.com/doc/api/line_items.html#method.lti/ims/line_items.create
|
78
95
|
def create(attrs = nil)
|
79
96
|
content_type = { "Content-Type" => "application/vnd.ims.lis.v2.lineitem+json" }
|
80
|
-
|
97
|
+
response, = service_post(endpoint(@id_token_decoded), body: JSON.dump(attrs), headers: headers(content_type))
|
98
|
+
response
|
81
99
|
end
|
82
100
|
|
83
101
|
# Update a line item
|
84
102
|
# Canvas: https://canvas.beta.instructure.com/doc/api/line_items.html#method.lti/ims/line_items.update
|
85
103
|
def update(line_item_url, attrs)
|
86
104
|
content_type = { "Content-Type" => "application/vnd.ims.lis.v2.lineitem+json" }
|
87
|
-
|
105
|
+
response, = service_put(line_item_url, body: JSON.dump(attrs), headers: headers(content_type))
|
106
|
+
response
|
88
107
|
end
|
89
108
|
|
90
109
|
def delete(line_item_url)
|
91
|
-
|
110
|
+
response, = service_delete(line_item_url, headers: headers)
|
111
|
+
response
|
92
112
|
end
|
93
113
|
end
|
94
114
|
end
|
@@ -49,16 +49,29 @@ module AtomicLti
|
|
49
49
|
uri.query_values = (uri.query_values || {}).merge(query)
|
50
50
|
uri.to_str
|
51
51
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
},
|
59
|
-
),
|
52
|
+
response, = service_get(
|
53
|
+
url,
|
54
|
+
headers: headers(
|
55
|
+
{
|
56
|
+
"Accept" => "application/vnd.ims.lti-nrps.v2.membershipcontainer+json",
|
57
|
+
},
|
60
58
|
),
|
61
59
|
)
|
60
|
+
|
61
|
+
verify_received_user_names(response)
|
62
|
+
end
|
63
|
+
|
64
|
+
def list_all(query: {})
|
65
|
+
page_body = nil
|
66
|
+
|
67
|
+
members = AtomicLti::PagingHelper.paginate_request do |next_link|
|
68
|
+
result_page = list(query: query, page_url: next_link)
|
69
|
+
page_body = JSON.parse(result_page.body)
|
70
|
+
[page_body["members"], get_next_url(result_page)]
|
71
|
+
end
|
72
|
+
|
73
|
+
page_body["members"] = members
|
74
|
+
page_body
|
62
75
|
end
|
63
76
|
|
64
77
|
def verify_received_user_names(names_and_roles_memberships)
|
@@ -7,14 +7,30 @@ module AtomicLti
|
|
7
7
|
[AtomicLti::Definitions::AGS_SCOPE_RESULT]
|
8
8
|
end
|
9
9
|
|
10
|
-
def list(line_item_id)
|
11
|
-
url =
|
12
|
-
|
10
|
+
def list(line_item_id, query: {}, page_url: nil)
|
11
|
+
url = if page_url.present?
|
12
|
+
page_url
|
13
|
+
else
|
14
|
+
uri = Addressable::URI.parse("#{line_item_id}/results")
|
15
|
+
uri.query_values = (uri.query_values || {}).merge(query)
|
16
|
+
uri.to_str
|
17
|
+
end
|
18
|
+
|
19
|
+
accept = { "Accept" => "application/vnd.ims.lis.v2.resultcontainer+json" }
|
20
|
+
response, = service_get(url, headers: headers(accept))
|
21
|
+
response
|
22
|
+
end
|
23
|
+
|
24
|
+
def list_all(line_item_id, query: {})
|
25
|
+
AtomicLti::PagingHelper.paginate_request do |next_link|
|
26
|
+
result_page = list(line_item_id, query: query, page_url: next_link)
|
27
|
+
[JSON.parse(result_page.body), get_next_url(result_page)]
|
28
|
+
end
|
13
29
|
end
|
14
30
|
|
15
|
-
def show(
|
16
|
-
|
17
|
-
|
31
|
+
def show(result_id)
|
32
|
+
response, = service_get(result_id, headers: headers)
|
33
|
+
response
|
18
34
|
end
|
19
35
|
|
20
36
|
end
|
@@ -190,7 +190,7 @@ module AtomicLti
|
|
190
190
|
state_verified: state_verified,
|
191
191
|
}
|
192
192
|
|
193
|
-
if !state_verified &&
|
193
|
+
if !state_verified && AtomicLti.use_post_message_storage
|
194
194
|
env["atomic.validated.state_validation"][:lti_storage_params] =
|
195
195
|
build_lti_storage_params(request, platform)
|
196
196
|
end
|
@@ -361,7 +361,7 @@ module AtomicLti
|
|
361
361
|
target: request.params["lti_storage_target"],
|
362
362
|
originSupportBroken: !AtomicLti.set_post_message_origin,
|
363
363
|
platformOIDCUrl: platform.oidc_url,
|
364
|
-
}
|
364
|
+
}.compact
|
365
365
|
end
|
366
366
|
end
|
367
367
|
end
|
data/lib/atomic_lti/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: atomic_lti
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Petro
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2023-
|
13
|
+
date: 2023-12-06 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: pg
|
@@ -71,6 +71,7 @@ files:
|
|
71
71
|
- app/lib/atomic_lti/exceptions.rb
|
72
72
|
- app/lib/atomic_lti/lti.rb
|
73
73
|
- app/lib/atomic_lti/open_id.rb
|
74
|
+
- app/lib/atomic_lti/paging_helper.rb
|
74
75
|
- app/lib/atomic_lti/params.rb
|
75
76
|
- app/lib/atomic_lti/role_enforcement_mode.rb
|
76
77
|
- app/lib/atomic_lti/services/base.rb
|
@@ -132,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
132
133
|
- !ruby/object:Gem::Version
|
133
134
|
version: '0'
|
134
135
|
requirements: []
|
135
|
-
rubygems_version: 3.4.
|
136
|
+
rubygems_version: 3.4.10
|
136
137
|
signing_key:
|
137
138
|
specification_version: 4
|
138
139
|
summary: AtomicLti implements the LTI Advantage specification.
|