d2l-valence 0.0.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 +7 -0
- data/.codeclimate.yml +22 -0
- data/.gitignore +17 -0
- data/.rubocop.yml +13 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +71 -0
- data/LICENSE.txt +22 -0
- data/README.md +266 -0
- data/Rakefile +6 -0
- data/d2l-valence.gemspec +30 -0
- data/lib/d2l/valence.rb +15 -0
- data/lib/d2l/valence/app_context.rb +49 -0
- data/lib/d2l/valence/auth_tokens.rb +60 -0
- data/lib/d2l/valence/encrypt.rb +34 -0
- data/lib/d2l/valence/host.rb +43 -0
- data/lib/d2l/valence/request.rb +104 -0
- data/lib/d2l/valence/response.rb +66 -0
- data/lib/d2l/valence/timestamp_error.rb +42 -0
- data/lib/d2l/valence/user_context.rb +58 -0
- data/lib/d2l/valence/version.rb +5 -0
- data/spec/d2l/valence/app_context_spec.rb +14 -0
- data/spec/d2l/valence/auth_tokens_spec.rb +30 -0
- data/spec/d2l/valence/host_spec.rb +64 -0
- data/spec/d2l/valence/request/authenticated_uri_spec.rb +75 -0
- data/spec/d2l/valence/request/execute/invalid_time_stamp_spec.rb +40 -0
- data/spec/d2l/valence/request/execute/lti_links/create_spec.rb +66 -0
- data/spec/d2l/valence/request/execute/lti_links/delete_spec.rb +39 -0
- data/spec/d2l/valence/request/execute/lti_links/list_spec.rb +41 -0
- data/spec/d2l/valence/request/execute/lti_links/update_spec.rb +73 -0
- data/spec/d2l/valence/request/execute/lti_links/view_spec.rb +41 -0
- data/spec/d2l/valence/request/execute/version_spec.rb +41 -0
- data/spec/d2l/valence/request/execute/whoami_spec.rb +40 -0
- data/spec/d2l/valence/response/code_spec.rb +34 -0
- data/spec/d2l/valence/response/server_skew_spec.rb +33 -0
- data/spec/d2l/valence/timestamp_error_spec.rb +21 -0
- data/spec/d2l/valence/user_context_spec.rb +6 -0
- data/spec/fixtures/vcr_cassettes/request/execute/create_lti_link.yml +71 -0
- data/spec/fixtures/vcr_cassettes/request/execute/delete_lti_link.yml +48 -0
- data/spec/fixtures/vcr_cassettes/request/execute/get_a_lti_link.yml +66 -0
- data/spec/fixtures/vcr_cassettes/request/execute/get_lti_links.yml +70 -0
- data/spec/fixtures/vcr_cassettes/request/execute/get_version.yml +66 -0
- data/spec/fixtures/vcr_cassettes/request/execute/get_whoami.yml +61 -0
- data/spec/fixtures/vcr_cassettes/request/execute/invalid_timestamp.yml +112 -0
- data/spec/fixtures/vcr_cassettes/request/execute/put_lti_link.yml +139 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/common_context.rb +37 -0
- metadata +228 -0
data/lib/d2l/valence.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'cgi'
|
3
|
+
require 'base64'
|
4
|
+
require 'openssl'
|
5
|
+
require 'restclient'
|
6
|
+
|
7
|
+
require 'd2l/valence/version'
|
8
|
+
require 'd2l/valence/host'
|
9
|
+
require 'd2l/valence/app_context'
|
10
|
+
require 'd2l/valence/encrypt'
|
11
|
+
require 'd2l/valence/timestamp_error'
|
12
|
+
require 'd2l/valence/response'
|
13
|
+
require 'd2l/valence/user_context'
|
14
|
+
require 'd2l/valence/auth_tokens'
|
15
|
+
require 'd2l/valence/request'
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module D2L
|
2
|
+
module Valence
|
3
|
+
# == AppContext
|
4
|
+
# Class with contextual detail for the application (the ruby client)
|
5
|
+
class AppContext
|
6
|
+
APP_ID_PARAM = 'x_a'.freeze
|
7
|
+
AUTH_KEY_PARAM = 'x_b'.freeze
|
8
|
+
CALLBACK_URL_PARAM = 'x_target'.freeze
|
9
|
+
AUTH_SERVICE_URI_PATH = '/d2l/auth/api/token'.freeze
|
10
|
+
|
11
|
+
attr_reader :brightspace_host,
|
12
|
+
:app_id,
|
13
|
+
:app_key,
|
14
|
+
:api_version
|
15
|
+
|
16
|
+
# @param [Valence::Host] brightspace_host Authenticating D2L Brightspace Instance
|
17
|
+
# @param [String] app_id Application ID provided by your D2L admin
|
18
|
+
# @param [String] app_key Application Key provided by your D2L admin
|
19
|
+
# @param [String] api_version Version of the Valence API is use
|
20
|
+
def initialize(brightspace_host:, app_id:, app_key:, api_version: '1.0')
|
21
|
+
@brightspace_host = brightspace_host
|
22
|
+
@app_id = app_id
|
23
|
+
@app_key = app_key
|
24
|
+
@api_version = api_version
|
25
|
+
end
|
26
|
+
|
27
|
+
# Generates a URL for authentication
|
28
|
+
#
|
29
|
+
# @param [URI::Generic] callback_uri URI to redirect to post authentication
|
30
|
+
# @return [String] URL for authentication
|
31
|
+
def auth_url(callback_uri)
|
32
|
+
@brightspace_host.to_uri(
|
33
|
+
path: AUTH_SERVICE_URI_PATH,
|
34
|
+
query: query_params_using(callback_url: callback_uri.to_s)
|
35
|
+
).to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def query_params_using(callback_url:)
|
41
|
+
{
|
42
|
+
APP_ID_PARAM => @app_id,
|
43
|
+
AUTH_KEY_PARAM => Encrypt.generate_from(@app_key, callback_url),
|
44
|
+
CALLBACK_URL_PARAM => CGI.escape(callback_url)
|
45
|
+
}.map { |p, v| "#{p}=#{v}" }.join('&')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module D2L
|
2
|
+
module Valence
|
3
|
+
# == AuthTokens
|
4
|
+
# Class to generate authentication tokens for D2L Valance API calls
|
5
|
+
class AuthTokens
|
6
|
+
APP_ID_PARAM = 'x_a'.freeze
|
7
|
+
USER_ID_PARAM = 'x_b'.freeze
|
8
|
+
SIGNATURE_BY_APP_KEY_PARAM = 'x_c'.freeze
|
9
|
+
SIGNATURE_BY_USER_KEY_PARAM = 'x_d'.freeze
|
10
|
+
TIMESTAMP_PARAM = 'x_t'.freeze
|
11
|
+
|
12
|
+
# @param [D2L::Valence::Request] request the authenticated request that the auth tokens are for
|
13
|
+
def initialize(request:)
|
14
|
+
@call = request
|
15
|
+
@user_context = @call.user_context
|
16
|
+
@app_context = @user_context.app_context
|
17
|
+
end
|
18
|
+
|
19
|
+
# Generates the auth tokens as a Hash for inclusion in the final URI query string
|
20
|
+
# @return [Hash] tokens for authenticated call
|
21
|
+
def generate
|
22
|
+
@tokens = {}
|
23
|
+
add_app_tokens
|
24
|
+
add_user_tokens
|
25
|
+
add_timestamp_token
|
26
|
+
@tokens
|
27
|
+
end
|
28
|
+
|
29
|
+
# Generates a timestamp with time skew between server and client taken into consideration
|
30
|
+
#
|
31
|
+
# @return [Integer] Server skew adjusted timestamp in seconds
|
32
|
+
def adjusted_timestamp
|
33
|
+
@adjusted_timestamp ||= Time.now.to_f.to_i + @user_context.server_skew
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def add_app_tokens
|
39
|
+
@tokens[APP_ID_PARAM] = @app_context.app_id
|
40
|
+
@tokens[SIGNATURE_BY_APP_KEY_PARAM] = Encrypt.generate_from(@app_context.app_key, signature)
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_user_tokens
|
44
|
+
return if @user_context.user_id.nil?
|
45
|
+
|
46
|
+
@tokens[USER_ID_PARAM] = @user_context.user_id
|
47
|
+
@tokens[SIGNATURE_BY_USER_KEY_PARAM] = Encrypt.generate_from(@user_context.user_key, signature)
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_timestamp_token
|
51
|
+
@tokens[TIMESTAMP_PARAM] = adjusted_timestamp
|
52
|
+
end
|
53
|
+
|
54
|
+
# Generates a signature requite by the D2L Valence API
|
55
|
+
def signature
|
56
|
+
@signature ||= "#{@call.http_method}&#{CGI.unescape(@call.path).downcase}&#{adjusted_timestamp}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module D2L
|
2
|
+
module Valence
|
3
|
+
# == Encrypt
|
4
|
+
# Class encrypt and encode data for transmission in a URL
|
5
|
+
class Encrypt
|
6
|
+
# Encrypt and encode data with provided key
|
7
|
+
#
|
8
|
+
# @param [String] key the key to encrypt with
|
9
|
+
# @param [String] data data to encrypt and encode
|
10
|
+
def self.generate_from(key, data)
|
11
|
+
encode(sign(key, data))
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.sign(key, data)
|
15
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), key, data)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.encode(digest)
|
19
|
+
Base64.urlsafe_encode64(digest, padding: false).strip
|
20
|
+
rescue
|
21
|
+
old_encode digest
|
22
|
+
end
|
23
|
+
|
24
|
+
# support for older versions of ruby
|
25
|
+
def self.old_encode(digest)
|
26
|
+
remove_unwanted_chars Base64.encode64(digest).strip
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.remove_unwanted_chars(string)
|
30
|
+
string.delete('=').tr('+', '-').tr('/', '_').strip
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module D2L
|
2
|
+
module Valence
|
3
|
+
# == Host
|
4
|
+
# Class for the creation of URIs for communication with the D2L Valence API
|
5
|
+
class Host
|
6
|
+
attr_accessor :scheme, :host, :port
|
7
|
+
|
8
|
+
# @param [Symbol] scheme URI scheme to be used (either :http or :https)
|
9
|
+
# @param [String] host host name for D2L server (e.g. d2l.myinstitution.com)
|
10
|
+
# @param [Integer] port specific port for transmission (optional)
|
11
|
+
def initialize(scheme:, host:, port: nil)
|
12
|
+
self.scheme = scheme
|
13
|
+
self.host = host
|
14
|
+
self.port = port
|
15
|
+
end
|
16
|
+
|
17
|
+
def host=(value)
|
18
|
+
raise 'Host cannot be nil' if value.nil?
|
19
|
+
@host = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def scheme=(value)
|
23
|
+
return if value.nil?
|
24
|
+
value = value.downcase.to_sym if value.is_a? String
|
25
|
+
raise "#{value} is an unsupported scheme. Please use either HTTP or HTTPS" unless supported_scheme? value
|
26
|
+
@scheme = value
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_uri(path: nil, query: nil)
|
30
|
+
{
|
31
|
+
http: URI::HTTP.build(host: host, port: port, path: path, query: query),
|
32
|
+
https: URI::HTTPS.build(host: host, port: port, path: path, query: query)
|
33
|
+
}[scheme]
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def supported_scheme?(value)
|
39
|
+
[:http, :https].include? value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module D2L
|
2
|
+
module Valence
|
3
|
+
# == Request
|
4
|
+
# Class for authenticated calls to the D2L Valence API
|
5
|
+
class Request
|
6
|
+
attr_reader :user_context,
|
7
|
+
:http_method,
|
8
|
+
:response
|
9
|
+
|
10
|
+
#
|
11
|
+
# == API routes
|
12
|
+
# See D2L::Valence::UserContext.api_call for details on creating routes and route_params
|
13
|
+
#
|
14
|
+
# @param [D2L::Valance::UserContext] user_context the user context created after authentication
|
15
|
+
# @param [String] http_method the HTTP Method for the call (i.e. PUT, GET, POST, DELETE)
|
16
|
+
# @param [String] route the API method route (e.g. /d2l/api/lp/:version/users/whoami)
|
17
|
+
# @param [Hash] route_params the parameters for the API method route (option)
|
18
|
+
# @param [Hash] query_params the query parameters for the method call
|
19
|
+
def initialize(user_context:, http_method:, route:, route_params: {}, query_params: {})
|
20
|
+
@user_context = user_context
|
21
|
+
@app_context = user_context.app_context
|
22
|
+
@http_method = http_method.upcase
|
23
|
+
@route = route
|
24
|
+
@route_params = route_params
|
25
|
+
@query_params = query_params
|
26
|
+
|
27
|
+
raise "HTTP Method #{@http_method} is unsupported" unless %w(GET PUT POST DELETE).include? @http_method
|
28
|
+
end
|
29
|
+
|
30
|
+
# Generates an authenticated URI for a the Valence API method
|
31
|
+
#
|
32
|
+
# @return [URI::Generic] URI for the authenticated method call
|
33
|
+
def authenticated_uri
|
34
|
+
@app_context.brightspace_host.to_uri(
|
35
|
+
path: path,
|
36
|
+
query: query
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Sends the authenticated call on the Valence API
|
41
|
+
#
|
42
|
+
# @return [D2L::Valence::Response] URI for the authenticated methof call
|
43
|
+
def execute
|
44
|
+
raise "HTTP Method #{@http_method} is not implemented" if params.nil?
|
45
|
+
|
46
|
+
@response = execute_call
|
47
|
+
@user_context.server_skew = @response.server_skew
|
48
|
+
@response
|
49
|
+
end
|
50
|
+
|
51
|
+
# Generates the final path for the authenticated call
|
52
|
+
#
|
53
|
+
# @return [String] path for the authenticated call
|
54
|
+
def path
|
55
|
+
return @path unless @path.nil?
|
56
|
+
|
57
|
+
substitute_keys_with(@route_params)
|
58
|
+
substitute_keys_with(known_params)
|
59
|
+
@path = @route
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def execute_call
|
65
|
+
Response.new RestClient.send(@http_method.downcase, *params)
|
66
|
+
rescue RestClient::Exception => e
|
67
|
+
Response.new e.response
|
68
|
+
end
|
69
|
+
|
70
|
+
def params
|
71
|
+
{
|
72
|
+
'GET' => [authenticated_uri.to_s],
|
73
|
+
'POST' => [authenticated_uri.to_s, @query_params.to_json, content_type: :json],
|
74
|
+
'PUT' => [authenticated_uri.to_s, @query_params.to_json, content_type: :json],
|
75
|
+
'DELETE' => [authenticated_uri.to_s, content_type: :json]
|
76
|
+
}[@http_method]
|
77
|
+
end
|
78
|
+
|
79
|
+
def substitute_keys_with(params)
|
80
|
+
params.each { |param, value| @route.gsub!(":#{param}", value.to_s) }
|
81
|
+
end
|
82
|
+
|
83
|
+
def known_params
|
84
|
+
{
|
85
|
+
version: @user_context.app_context.api_version
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def query
|
90
|
+
return to_query_params(authenticated_tokens) unless @http_method == 'GET'
|
91
|
+
|
92
|
+
to_query_params @query_params.merge(authenticated_tokens)
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_query_params(hash)
|
96
|
+
hash.map { |k, v| "#{k}=#{v}" }.join('&')
|
97
|
+
end
|
98
|
+
|
99
|
+
def authenticated_tokens
|
100
|
+
D2L::Valence::AuthTokens.new(request: self).generate
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module D2L
|
2
|
+
module Valence
|
3
|
+
# == Response
|
4
|
+
# Class for interpreting the response from the D2L Valence API
|
5
|
+
class Response
|
6
|
+
attr_reader :http_response
|
7
|
+
|
8
|
+
# @param [RestClient::Response] http_response response from a request against the Valance API
|
9
|
+
def initialize(http_response)
|
10
|
+
@http_response = http_response
|
11
|
+
@server_skew = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [String] Plain text response from the D2L server
|
15
|
+
def body
|
16
|
+
@http_response.body
|
17
|
+
end
|
18
|
+
|
19
|
+
# Generates a hash based on a valid JSON response from the D2L server. If the provided response is not in a
|
20
|
+
# value JSON format then an empty hash is returned
|
21
|
+
#
|
22
|
+
# @return [Hash] hash based on JSON body
|
23
|
+
def to_hash
|
24
|
+
@to_hash ||= JSON.parse(body)
|
25
|
+
rescue
|
26
|
+
@to_hash = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Symbol] the interpreted code for the Valance API response
|
30
|
+
def code
|
31
|
+
interpret_forbidden || http_code
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Integer] the difference in D2L Valance API Server time and local time
|
35
|
+
def server_skew
|
36
|
+
return 0 unless timestamp_error.timestamp_out_of_range?
|
37
|
+
|
38
|
+
@server_skew = timestamp_error.server_skew
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def http_code
|
44
|
+
"HTTP_#{@http_response.code}".to_sym
|
45
|
+
end
|
46
|
+
|
47
|
+
def interpret_forbidden
|
48
|
+
return unless @http_response.code == 403
|
49
|
+
|
50
|
+
invalid_timestamp || invalid_token
|
51
|
+
end
|
52
|
+
|
53
|
+
def invalid_timestamp
|
54
|
+
:INVALID_TIMESTAMP if timestamp_error.timestamp_out_of_range?
|
55
|
+
end
|
56
|
+
|
57
|
+
def timestamp_error
|
58
|
+
@timestamp_error ||= TimestampError.new(@http_response.body)
|
59
|
+
end
|
60
|
+
|
61
|
+
def invalid_token
|
62
|
+
:INVALID_TOKEN if @http_response.body.include? 'invalid token'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module D2L
|
2
|
+
module Valence
|
3
|
+
# == TimestampError
|
4
|
+
# This class is aimed at parsing and providing diagnostics for time based issues
|
5
|
+
# between the D2L Brightspace Server and Ruby Client
|
6
|
+
class TimestampError
|
7
|
+
# @param [String] D2L Brightspace Server Error Message
|
8
|
+
def initialize(error_message)
|
9
|
+
@error_message = error_message
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [Integer] difference in D2L Server timestamp in seconds
|
13
|
+
def server_skew
|
14
|
+
return 0 if server_time_in_seconds.nil?
|
15
|
+
|
16
|
+
@server_skew ||= server_time_in_seconds - now_in_seconds
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Integer] true if our timestamp is out of range with the D2L Server
|
20
|
+
def timestamp_out_of_range?
|
21
|
+
server_time_in_seconds != nil
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# @return [Integer] D2L Server timestamp
|
27
|
+
def server_time_in_seconds
|
28
|
+
@server_timestamp ||= parse_timestamp
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [Integer] local timestamp now in seconds
|
32
|
+
def now_in_seconds
|
33
|
+
Time.now.to_f.to_i
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_timestamp
|
37
|
+
match = Regexp.new(/Timestamp out of range\s*(\d+)/).match(@error_message)
|
38
|
+
match[1].to_i if !match.nil? && match.length >= 2
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module D2L
|
2
|
+
module Valence
|
3
|
+
# == UserContext
|
4
|
+
# Instances of this class are used to make D2L Valance API calls with the current user credentials
|
5
|
+
class UserContext
|
6
|
+
attr_reader :app_context,
|
7
|
+
:user_id,
|
8
|
+
:user_key
|
9
|
+
|
10
|
+
attr_accessor :server_skew # in seconds
|
11
|
+
|
12
|
+
# @param [D2L::Valence::AppContext] app_context
|
13
|
+
# @param [String] user_id User ID returned from the D2L Server in authentication process
|
14
|
+
# @param [String] user_key User Key returned the D2L Server in authentication process
|
15
|
+
def initialize(app_context:, user_id:, user_key:)
|
16
|
+
@app_context = app_context
|
17
|
+
@user_id = user_id
|
18
|
+
@user_key = user_key
|
19
|
+
@server_skew = 0
|
20
|
+
end
|
21
|
+
|
22
|
+
# Calls a API method on the Valance API
|
23
|
+
#
|
24
|
+
# == API routes
|
25
|
+
# When providing the route you can also provide the variables for the parameters in the route. For example, the
|
26
|
+
# following route will require `{org_unit_id: 1, group_category_id: 23}` for `route_params`:
|
27
|
+
#
|
28
|
+
# /d2l/api/lp/:version/:org_unit_id/groupcategories/:group_category_id
|
29
|
+
#
|
30
|
+
# The `to_uri` method will place the parameters in the route to make the final path. For example:
|
31
|
+
#
|
32
|
+
# /d2l/api/lp/1.0/1/groupcategories/23
|
33
|
+
#
|
34
|
+
# There are known parameters such as `:version` which is provided when you create your initial AppContext
|
35
|
+
# instance. This will mean that some routes will require no parameters for example:
|
36
|
+
#
|
37
|
+
# /d2l/api/lp/:version/users/whoami
|
38
|
+
#
|
39
|
+
# which becomes
|
40
|
+
# /d2l/api/lp/1.0/users/whoami
|
41
|
+
#
|
42
|
+
# @param [String] http_method the HTTP Method for the call (i.e. PUT, GET, POST, DELETE)
|
43
|
+
# @param [String] route the API method route (e.g. /d2l/api/lp/:version/users/whoami)
|
44
|
+
# @param [Hash] route_params the parameters for the API method route (option)
|
45
|
+
# @param [Hash] query_params the query parameters for the method call
|
46
|
+
# @return [D2L::Valence::RequestResult] returns a request
|
47
|
+
def api_call(http_method:, route:, route_params:, query_params:)
|
48
|
+
D2L::Valence::Request.new(
|
49
|
+
user_context: self,
|
50
|
+
http_method: http_method,
|
51
|
+
route: route,
|
52
|
+
route_params: route_params,
|
53
|
+
query_params: query_params
|
54
|
+
).execute
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|