bearcat 1.4.12 → 1.5.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bearcat.gemspec +9 -3
- data/lib/bearcat/api_array.rb +2 -0
- data/lib/bearcat/client/account_reports.rb +6 -14
- data/lib/bearcat/client/accounts.rb +20 -56
- data/lib/bearcat/client/analytics.rb +4 -7
- data/lib/bearcat/client/assignment_groups.rb +7 -19
- data/lib/bearcat/client/assignments.rb +15 -39
- data/lib/bearcat/client/blueprint_courses.rb +14 -20
- data/lib/bearcat/client/calendar_events.rb +9 -17
- data/lib/bearcat/client/canvas_files.rb +0 -2
- data/lib/bearcat/client/conferences.rb +3 -8
- data/lib/bearcat/client/conversations.rb +3 -8
- data/lib/bearcat/client/courses.rb +15 -45
- data/lib/bearcat/client/custom_gradebook_columns.rb +11 -25
- data/lib/bearcat/client/discussions.rb +11 -37
- data/lib/bearcat/client/enrollments.rb +9 -29
- data/lib/bearcat/client/external_tools.rb +9 -36
- data/lib/bearcat/client/file_helper.rb +3 -3
- data/lib/bearcat/client/files.rb +2 -4
- data/lib/bearcat/client/folders.rb +13 -59
- data/lib/bearcat/client/group_categories.rb +8 -17
- data/lib/bearcat/client/group_memberships.rb +6 -14
- data/lib/bearcat/client/groups.rb +9 -24
- data/lib/bearcat/client/module_items.rb +9 -17
- data/lib/bearcat/client/modules.rb +10 -20
- data/lib/bearcat/client/outcome_groups.rb +2 -4
- data/lib/bearcat/client/outcomes.rb +4 -7
- data/lib/bearcat/client/pages.rb +8 -23
- data/lib/bearcat/client/progresses.rb +2 -3
- data/lib/bearcat/client/quizzes.rb +15 -35
- data/lib/bearcat/client/reports.rb +8 -19
- data/lib/bearcat/client/roles.rb +1 -1
- data/lib/bearcat/client/rubric.rb +8 -20
- data/lib/bearcat/client/rubric_assessment.rb +5 -11
- data/lib/bearcat/client/rubric_association.rb +6 -12
- data/lib/bearcat/client/search.rb +2 -4
- data/lib/bearcat/client/sections.rb +11 -26
- data/lib/bearcat/client/submissions.rb +48 -44
- data/lib/bearcat/client/tabs.rb +4 -7
- data/lib/bearcat/client/users.rb +17 -44
- data/lib/bearcat/client.rb +72 -44
- data/lib/bearcat/client_module.rb +101 -0
- data/lib/bearcat/rate_limiting/redis_script.rb +164 -0
- data/lib/bearcat/rate_limiting.rb +70 -0
- data/lib/bearcat/spec_helpers.rb +62 -31
- data/lib/bearcat/version.rb +1 -1
- data/lib/bearcat.rb +8 -21
- data/lib/catalogcat/api_array.rb +18 -0
- data/lib/catalogcat/client/catalogs.rb +21 -0
- data/lib/catalogcat/client/certificates.rb +9 -0
- data/lib/catalogcat/client/courses.rb +25 -0
- data/lib/catalogcat/client/orders.rb +9 -0
- data/lib/catalogcat/client.rb +39 -0
- data/lib/catalogcat/version.rb +3 -0
- data/lib/catalogcat.rb +14 -0
- data/spec/bearcat/client/canvas_files_spec.rb +1 -2
- data/spec/bearcat/client/content_migrations_spec.rb +2 -2
- data/spec/bearcat/client/courses_spec.rb +2 -4
- data/spec/bearcat/{group_memberships_spec.rb → client/group_memberships_spec.rb} +0 -0
- data/spec/bearcat/client/learning_outcomes_spec.rb +2 -2
- data/spec/bearcat/client/submissions_spec.rb +14 -0
- data/spec/bearcat/client_spec.rb +1 -4
- data/spec/bearcat/rate_limiting_spec.rb +62 -0
- data/spec/bearcat/stub_bearcat_spec.rb +15 -0
- data/spec/helper.rb +1 -0
- metadata +196 -181
@@ -1,19 +1,13 @@
|
|
1
1
|
module Bearcat
|
2
2
|
class Client < Footrest::Client
|
3
3
|
module RubricAssessment
|
4
|
+
extend ClientModule
|
4
5
|
|
5
|
-
|
6
|
-
post
|
6
|
+
prefix "/api/v1/courses/:course/rubric_associations/:rubric_association/rubric_assessments/" do
|
7
|
+
post :create_course_rubric_assessment
|
8
|
+
put :update_course_rubric_assessment, ":rubric_assessment"
|
9
|
+
delete :delete_course_rubric_assessment, ":rubric_assessment"
|
7
10
|
end
|
8
|
-
|
9
|
-
def update_course_rubric_assessment(course, rubric_association, rubric_assessment, params={})
|
10
|
-
put("api/v1/courses/#{course}/rubric_associations/#{rubric_association}/rubric_assessments/#{rubric_assessment}", params)
|
11
|
-
end
|
12
|
-
|
13
|
-
def delete_course_rubric_assessment(course, rubric_association, rubric_assessment)
|
14
|
-
delete("api/v1/courses/#{course}/rubric_associations/#{rubric_association}/rubric_assessments/#{rubric_assessment}")
|
15
|
-
end
|
16
|
-
|
17
11
|
end
|
18
12
|
end
|
19
13
|
end
|
@@ -1,19 +1,13 @@
|
|
1
1
|
module Bearcat
|
2
2
|
class Client < Footrest::Client
|
3
3
|
module RubricAssociation
|
4
|
+
extend ClientModule
|
4
5
|
|
5
|
-
|
6
|
-
post
|
6
|
+
prefix "/api/v1/courses/:course/rubric_associations/" do
|
7
|
+
post :create_course_rubric_association
|
8
|
+
put :update_course_rubric_association, ":rubric_association"
|
9
|
+
delete :delete_course_rubric_association, ":rubric_association"
|
7
10
|
end
|
8
|
-
|
9
|
-
def update_course_rubric_association(course, rubric_association, params={})
|
10
|
-
put("api/v1/courses/#{course}/rubric_associations/#{rubric_association}", params)
|
11
|
-
end
|
12
|
-
|
13
|
-
def delete_course_rubric_association(course, rubric_association)
|
14
|
-
delete("api/v1/courses/#{course}/rubric_associations/#{rubric_association}")
|
15
|
-
end
|
16
|
-
|
17
11
|
end
|
18
12
|
end
|
19
|
-
end
|
13
|
+
end
|
@@ -1,11 +1,9 @@
|
|
1
1
|
module Bearcat
|
2
2
|
class Client < Footrest::Client
|
3
3
|
module Search
|
4
|
+
extend ClientModule
|
4
5
|
|
5
|
-
|
6
|
-
get('/api/v1/conversations/find_recipients', params)
|
7
|
-
end
|
8
|
-
|
6
|
+
get :find_recipients, '/api/v1/conversations/find_recipients'
|
9
7
|
end
|
10
8
|
end
|
11
9
|
end
|
@@ -1,35 +1,20 @@
|
|
1
1
|
module Bearcat
|
2
2
|
class Client < Footrest::Client
|
3
3
|
module Sections
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def create_section(course, params)
|
14
|
-
post("/api/v1/courses/#{course.to_s}/sections", params)
|
15
|
-
end
|
16
|
-
|
17
|
-
def update_section(section, params)
|
18
|
-
put("/api/v1/sections/#{section.to_s}", params)
|
19
|
-
end
|
20
|
-
|
21
|
-
def delete_section(section)
|
22
|
-
delete("/api/v1/sections/#{section.to_s}")
|
4
|
+
extend ClientModule
|
5
|
+
|
6
|
+
prefix "/api/v1/sections/:section/" do
|
7
|
+
get :section
|
8
|
+
put :update_section
|
9
|
+
delete :delete_section
|
10
|
+
post :crosslist_section, "crosslist/:new_course_id"
|
11
|
+
delete :decrosslist_section, "crosslist"
|
23
12
|
end
|
24
13
|
|
25
|
-
|
26
|
-
|
14
|
+
prefix "/api/v1/courses/:course/sections/" do
|
15
|
+
get :course_sections
|
16
|
+
post :create_section
|
27
17
|
end
|
28
|
-
|
29
|
-
def decrosslist_section(section_id)
|
30
|
-
delete("/api/v1/sections/#{section_id.to_s}/crosslist")
|
31
|
-
end
|
32
|
-
|
33
18
|
end
|
34
19
|
end
|
35
20
|
end
|
@@ -1,65 +1,69 @@
|
|
1
1
|
module Bearcat
|
2
2
|
class Client < Footrest::Client
|
3
3
|
module Submissions
|
4
|
+
extend ClientModule
|
4
5
|
|
5
|
-
|
6
|
-
get
|
6
|
+
prefix "/api/v1/courses/:course/assignments/:assignment/submissions/" do
|
7
|
+
get :get_course_submissions
|
8
|
+
get :user_course_assignment_submission, ":user"
|
7
9
|
end
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def get_course_submissions(course, assignment, params={})
|
14
|
-
get("/api/v1/courses/#{course.to_s}/assignments/#{assignment}/submissions", params)
|
15
|
-
end
|
16
|
-
|
17
|
-
def user_course_assignment_submission(course, assignment, user, params={})
|
18
|
-
get("/api/v1/courses/#{course.to_s}/assignments/#{assignment.to_s}/submissions/#{user.to_s}", params)
|
19
|
-
end
|
11
|
+
context_types %i[course section] do |ct|
|
12
|
+
prefix "/api/v1/#{ct}s/:#{ct}/" do
|
13
|
+
get :"#{ct}_submissions", "students/submissions"
|
20
14
|
|
21
|
-
|
22
|
-
|
15
|
+
prefix "assignments/:assignment/submissions/" do
|
16
|
+
post :"#{ct}_submission"
|
17
|
+
put :"grade_#{ct}_submission", ":user"
|
18
|
+
post :"#{ct}_update_grades", "update_grades"
|
19
|
+
end
|
20
|
+
end
|
23
21
|
end
|
24
22
|
|
25
|
-
def
|
26
|
-
|
23
|
+
def course_file_upload_submission(course, assignment, user, file_data, params={})
|
24
|
+
file_upload_submission(course, assignment, user, file_data, params, type: :course)
|
27
25
|
end
|
28
26
|
|
29
|
-
def
|
30
|
-
|
27
|
+
def section_file_upload_submission(section, assignment, user, file_data, params={})
|
28
|
+
file_upload_submission(section, assignment, user, file_data, params, type: :section)
|
31
29
|
end
|
32
30
|
|
33
|
-
|
34
|
-
put("/api/v1/sections/#{section}/assignments/#{assignment}/submissions/#{user}", params)
|
35
|
-
end
|
31
|
+
protected
|
36
32
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
'file_ids'=> [response['id']]
|
42
|
-
}
|
43
|
-
course_submission(course, assignment, params)
|
44
|
-
end
|
33
|
+
# @param file_data One of an array of file_path strings or an array of Hashes, each being upload file params plus the file's path
|
34
|
+
def file_upload_submission(type_id, assignment, user, file_data=nil, params={}, type:)
|
35
|
+
raise ArgumentError, 'Invalid type' unless [:course, :section].include?(type)
|
36
|
+
raise ArgumentError, 'Must provide either file paths or fully formed submission params' if params.dig(:submission, :file_ids).nil? && file_data.nil?
|
45
37
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
38
|
+
params = params.with_indifferent_access
|
39
|
+
sub_params = if params.dig(:submission, :file_ids).present?
|
40
|
+
{}
|
41
|
+
else
|
42
|
+
file_data = Array.wrap(file_data)
|
43
|
+
file_ids = file_data.map do |datum|
|
44
|
+
if datum.is_a?(Hash) && datum[:file_path].present?
|
45
|
+
# datum is a param hash for upload_file API plus the file_path
|
46
|
+
path = datum[:file_path]
|
47
|
+
upload_params = datum.except(:file_path)
|
48
|
+
else
|
49
|
+
# treat datum as a file_path string
|
50
|
+
path = datum
|
51
|
+
upload_params = params
|
52
|
+
end
|
54
53
|
|
55
|
-
|
56
|
-
|
57
|
-
|
54
|
+
response = upload_file("/api/v1/#{type}s/#{type_id}/assignments/#{assignment}/submissions/#{user}/files", path, upload_params)
|
55
|
+
response['id']
|
56
|
+
end
|
58
57
|
|
59
|
-
|
60
|
-
|
58
|
+
{
|
59
|
+
submission_type: 'online_upload',
|
60
|
+
file_ids: file_ids,
|
61
|
+
}
|
62
|
+
end
|
63
|
+
sub_params.merge!(params[:submission]) if params[:submission].present?
|
64
|
+
params[:submission] = sub_params
|
65
|
+
send("#{type}_submission".to_sym, type_id, assignment, params)
|
61
66
|
end
|
62
|
-
|
63
67
|
end
|
64
68
|
end
|
65
69
|
end
|
data/lib/bearcat/client/tabs.rb
CHANGED
@@ -1,15 +1,12 @@
|
|
1
1
|
module Bearcat
|
2
2
|
class Client < Footrest::Client
|
3
3
|
module Tabs
|
4
|
+
extend ClientModule
|
4
5
|
|
5
|
-
|
6
|
-
get
|
6
|
+
prefix "/api/v1/courses/:course/tabs/" do
|
7
|
+
get :get_tabs
|
8
|
+
put :update_tab, ":tab"
|
7
9
|
end
|
8
|
-
|
9
|
-
def update_tab(id, course, params={})
|
10
|
-
put("/api/v1/courses/#{course}/tabs/#{id}", params)
|
11
|
-
end
|
12
|
-
|
13
10
|
end
|
14
11
|
end
|
15
12
|
end
|
data/lib/bearcat/client/users.rb
CHANGED
@@ -1,33 +1,26 @@
|
|
1
1
|
module Bearcat
|
2
2
|
class Client < Footrest::Client
|
3
3
|
module Users
|
4
|
+
extend ClientModule
|
4
5
|
|
5
|
-
|
6
|
-
get
|
6
|
+
prefix "/api/v1/accounts/:account/users/" do
|
7
|
+
get :list_users
|
8
|
+
post :add_user
|
7
9
|
end
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
get("/api/v1/users/#{user.to_s}/profile", params)
|
23
|
-
end
|
24
|
-
|
25
|
-
def user_logins(user, params={})
|
26
|
-
get("/api/v1/users/#{user.to_s}/logins", params)
|
27
|
-
end
|
28
|
-
|
29
|
-
def communication_channels(user, params={})
|
30
|
-
get("/api/v1/users/#{user.to_s}/communication_channels", params)
|
11
|
+
prefix "/api/v1/users/" do
|
12
|
+
prefix ":user/" do
|
13
|
+
get :user_detail
|
14
|
+
get :user_avatars, "avatars"
|
15
|
+
get :user_profile, "profile"
|
16
|
+
get :user_logins, "logins"
|
17
|
+
get :communication_channels, "communication_channels"
|
18
|
+
get :page_views, "page_views"
|
19
|
+
put :user_merge, "merge_into/:merge_into_user"
|
20
|
+
get :user_assignments, "courses/:course/assignments"
|
21
|
+
get :dashboard_positions, "dashboard_positions"
|
22
|
+
put :update_dashboard_positions, "dashboard_positions"
|
23
|
+
end
|
31
24
|
end
|
32
25
|
|
33
26
|
# scope: food
|
@@ -52,26 +45,6 @@ module Bearcat
|
|
52
45
|
delete("/api/v1/users/#{user}/custom_data/#{scope}", params)
|
53
46
|
end
|
54
47
|
|
55
|
-
def page_views(user, params = {})
|
56
|
-
get("/api/v1/users/#{user}/page_views", params)
|
57
|
-
end
|
58
|
-
|
59
|
-
def user_merge(user, merge_into_user)
|
60
|
-
put("/api/v1/users/#{user}/merge_into/#{merge_into_user}")
|
61
|
-
end
|
62
|
-
|
63
|
-
def user_assignments(user, course, params = {})
|
64
|
-
get("/api/v1/users/#{user}/courses/#{course}/assignments", params)
|
65
|
-
end
|
66
|
-
|
67
|
-
def dashboard_positions(user, params = {})
|
68
|
-
get("/api/v1/users/#{user}/dashboard_positions", params)
|
69
|
-
end
|
70
|
-
|
71
|
-
def update_dashboard_positions(user, params = {})
|
72
|
-
put("/api/v1/users/#{user}/dashboard_positions", params)
|
73
|
-
end
|
74
|
-
|
75
48
|
def favorite_courses(user, params = {})
|
76
49
|
params.merge!({as_user_id: user})
|
77
50
|
get("/api/v1/users/self/favorites/courses", params)
|
data/lib/bearcat/client.rb
CHANGED
@@ -1,76 +1,104 @@
|
|
1
1
|
require 'active_support/core_ext/hash'
|
2
2
|
require 'footrest/client'
|
3
|
-
|
3
|
+
require_relative 'rate_limiting'
|
4
|
+
require_relative 'client_module'
|
4
5
|
|
5
6
|
module Bearcat
|
6
7
|
class Client < Footrest::Client
|
7
8
|
require 'bearcat/api_array'
|
8
9
|
|
10
|
+
@added_modules = []
|
11
|
+
|
9
12
|
Dir[File.join(__dir__, 'client', '*.rb')].each do |mod|
|
10
13
|
mname = File.basename(mod, '.*').camelize
|
11
14
|
mname = 'GraphQL' if mname == 'GraphQl'
|
12
15
|
require mod
|
13
|
-
|
16
|
+
lmod = "Bearcat::Client::#{mname}".constantize
|
17
|
+
include lmod
|
18
|
+
@added_modules << lmod
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.registered_endpoints
|
22
|
+
@registered_endpoints ||= @added_modules.reduce({}) do |h, m|
|
23
|
+
h.merge!(m._registered_endpoints) rescue h
|
24
|
+
end
|
25
|
+
@registered_endpoints
|
14
26
|
end
|
15
27
|
|
16
|
-
# Override Footrest request for ApiArray support
|
17
28
|
def request(method, &block)
|
18
|
-
|
19
|
-
|
20
|
-
|
29
|
+
response = rate_limited_request do
|
30
|
+
connection.send(method, &block)
|
31
|
+
end
|
21
32
|
ApiArray.process_response(response, self)
|
22
33
|
end
|
23
34
|
|
24
|
-
|
25
|
-
return unless limit_remaining.present?
|
26
|
-
return unless limit_remaining < Bearcat.rate_limit_min
|
35
|
+
protected
|
27
36
|
|
28
|
-
|
29
|
-
|
30
|
-
tts = Bearcat.max_sleep_seconds if tts > Bearcat.max_sleep_seconds
|
37
|
+
def rate_limited_request
|
38
|
+
return yield unless rate_limiter
|
31
39
|
|
40
|
+
canvas_rate_limits= 0
|
41
|
+
response = nil
|
32
42
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
43
|
+
begin
|
44
|
+
rate_limiter.apply(
|
45
|
+
rate_limit_key,
|
46
|
+
max_sleep: Bearcat.max_sleep_seconds,
|
47
|
+
on_sleep: ->(tts) {
|
48
|
+
message = "Canvas API rate limit reached; sleeping for #{tts.to_i} second(s) to catch up."
|
49
|
+
Bearcat.logger.debug(message)
|
50
|
+
},
|
51
|
+
) do
|
52
|
+
response = yield
|
53
|
+
0
|
54
|
+
end
|
55
|
+
rescue Footrest::HttpError::Forbidden => err
|
56
|
+
# Somehow our rate-limiting didn't limit enough and Canvas stopped us.
|
57
|
+
response = err.response
|
58
|
+
if canvas_rate_limits < 2 && err.message.include?("(Rate Limit Exceeded)")
|
59
|
+
canvas_rate_limits += 1
|
60
|
+
rate_limiter.checkin_known(rate_limit_key, 0)
|
37
61
|
|
38
|
-
|
39
|
-
|
62
|
+
message = "Canvas API applied rate limit; upticking Bearcat rate-limit avoidance and retrying (Retry #{canvas_rate_limits})."
|
63
|
+
Bearcat.logger.debug(message)
|
40
64
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
65
|
+
retry
|
66
|
+
end
|
67
|
+
raise
|
68
|
+
ensure
|
69
|
+
headers = response.try(:response_headers) || response.try(:headers) || {}
|
70
|
+
# -50 to provide a little extra leeway and hopefully be more proactive, making sure we don't even get close to Canvas throwing a 403, even if an out-of-band process is involved
|
71
|
+
rate_limiter.checkin_known(rate_limit_key, headers['x-rate-limit-remaining'].to_f - 100) if response
|
72
|
+
end
|
45
73
|
|
46
|
-
|
47
|
-
config[:master_rate_limit].present? || Bearcat.master_rate_limit.present?
|
74
|
+
response
|
48
75
|
end
|
49
76
|
|
50
|
-
def
|
51
|
-
|
52
|
-
Bearcat.
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
58
|
-
Bearcat.logger.debug limit['current'].to_s
|
59
|
-
limit['current']
|
77
|
+
def rate_limiter
|
78
|
+
@rate_limiter ||= begin
|
79
|
+
rl = config[:rate_limiter] || Bearcat.rate_limiter
|
80
|
+
master_rate_limit = config[:master_rate_limit].present? ? config[:master_rate_limit] : Bearcat.master_rate_limit
|
81
|
+
|
82
|
+
if rl.nil? && master_rate_limit.nil? && defined?(Rails) && Rails.env.production?
|
83
|
+
master_rate_limit = true if defined?(::Sidekiq)
|
60
84
|
end
|
61
|
-
else
|
62
|
-
Bearcat.rate_limits[config[:token]]
|
63
|
-
end
|
64
|
-
end
|
65
85
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
86
|
+
if rl.nil? && master_rate_limit
|
87
|
+
rl = RateLimiting::RedisLimiter
|
88
|
+
end
|
89
|
+
|
90
|
+
if rl.is_a?(Class)
|
91
|
+
rl.new()
|
92
|
+
elsif rl.present?
|
93
|
+
rl
|
70
94
|
end
|
71
|
-
else
|
72
|
-
Bearcat.rate_limits[config[:token]] = value
|
73
95
|
end
|
74
96
|
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def rate_limit_key
|
101
|
+
Digest::SHA1.hexdigest(config[:token])
|
102
|
+
end
|
75
103
|
end
|
76
104
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Bearcat
|
2
|
+
class Client < Footrest::Client
|
3
|
+
module ClientModule
|
4
|
+
ARG_REGEX = /:(\w+)/
|
5
|
+
attr_reader :_registered_endpoints
|
6
|
+
|
7
|
+
def prefix(prefix)
|
8
|
+
past_prefix = @current_prefix
|
9
|
+
@current_prefix = (past_prefix || '') + prefix
|
10
|
+
yield
|
11
|
+
ensure
|
12
|
+
@current_prefix = past_prefix
|
13
|
+
end
|
14
|
+
|
15
|
+
def endpoint(method, identifier, url = "", defaults: {}, &blk)
|
16
|
+
if @current_prefix && !url.start_with?('/')
|
17
|
+
url = url[2..] if url.start_with?('./')
|
18
|
+
url = @current_prefix + url
|
19
|
+
end
|
20
|
+
|
21
|
+
args = url.to_enum(:scan, ARG_REGEX).map { Regexp.last_match }
|
22
|
+
arg_names = args.map{|m| m[1]}
|
23
|
+
|
24
|
+
@_registered_endpoints ||= {}
|
25
|
+
@_registered_endpoints[identifier] = { symbol: identifier, method: method, url: url }
|
26
|
+
|
27
|
+
# TODO: Consider generating the method using class_eval and a template - this will improve runtime performance
|
28
|
+
# signature_bits = []
|
29
|
+
# logical_bits = []
|
30
|
+
# args.each do |m|
|
31
|
+
# name = [1]
|
32
|
+
# end
|
33
|
+
|
34
|
+
# interpolated_url = url.gsub(ARG_REGEX) do |m|
|
35
|
+
# '#{' + m[1] + '}'
|
36
|
+
# end
|
37
|
+
|
38
|
+
# class_eval <<~RUBY
|
39
|
+
# def #{identifier}(*args, **kwargs)
|
40
|
+
# parameters = {
|
41
|
+
|
42
|
+
# }
|
43
|
+
|
44
|
+
# #{method}(#{interpolated_url})
|
45
|
+
# end
|
46
|
+
# RUBY
|
47
|
+
|
48
|
+
define_method(identifier) do |*args, **kwargs|
|
49
|
+
url_arguments = {}
|
50
|
+
parameters = { }.with_indifferent_access
|
51
|
+
parameters.merge!(defaults)
|
52
|
+
|
53
|
+
args.each_with_index do |v, i|
|
54
|
+
if arg_names[i]
|
55
|
+
url_arguments[arg_names[i]] = v
|
56
|
+
elsif i == arg_names.count && v.is_a?(Hash)
|
57
|
+
parameters.merge!(v)
|
58
|
+
else
|
59
|
+
raise ArgumentError, "Too many arguments passed to #{identifier}" unless arg_names[i]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
parameters.merge!(kwargs)
|
64
|
+
|
65
|
+
preq = BearcatRequest.new(url, url_arguments, parameters)
|
66
|
+
yield preq if block_given?
|
67
|
+
|
68
|
+
arg_names.each do |an|
|
69
|
+
preq.arguments[an] = preq.parameters.delete(an) unless preq.arguments.key?(an)
|
70
|
+
raise ArgumentError, "Missing argument #{an}" unless preq.arguments.key?(an)
|
71
|
+
end
|
72
|
+
|
73
|
+
send(method, preq.interpoated_url, preq.parameters)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def context_types(types, &blk)
|
78
|
+
types.each(&blk)
|
79
|
+
end
|
80
|
+
|
81
|
+
%i[get post delete put patch head].each do |mthd|
|
82
|
+
define_method(mthd) do |*args, **kwargs, &blk|
|
83
|
+
endpoint(mthd, *args, **kwargs, &blk)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
BearcatRequest = Struct.new(:url, :arguments, :parameters) do
|
88
|
+
def interpoated_url
|
89
|
+
url.gsub(ARG_REGEX) do |_|
|
90
|
+
m = Regexp.last_match
|
91
|
+
val = arguments[m[1]]
|
92
|
+
val = val.canvas_id if val.respond_to?(:canvas_id)
|
93
|
+
val = val.id if val.respond_to?(:id)
|
94
|
+
val = val['id'].presence || val[:id].presence || val if val.is_a?(Hash)
|
95
|
+
val
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|