d2l-valence 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +22 -0
  3. data/.gitignore +17 -0
  4. data/.rubocop.yml +13 -0
  5. data/Gemfile +4 -0
  6. data/Gemfile.lock +71 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +266 -0
  9. data/Rakefile +6 -0
  10. data/d2l-valence.gemspec +30 -0
  11. data/lib/d2l/valence.rb +15 -0
  12. data/lib/d2l/valence/app_context.rb +49 -0
  13. data/lib/d2l/valence/auth_tokens.rb +60 -0
  14. data/lib/d2l/valence/encrypt.rb +34 -0
  15. data/lib/d2l/valence/host.rb +43 -0
  16. data/lib/d2l/valence/request.rb +104 -0
  17. data/lib/d2l/valence/response.rb +66 -0
  18. data/lib/d2l/valence/timestamp_error.rb +42 -0
  19. data/lib/d2l/valence/user_context.rb +58 -0
  20. data/lib/d2l/valence/version.rb +5 -0
  21. data/spec/d2l/valence/app_context_spec.rb +14 -0
  22. data/spec/d2l/valence/auth_tokens_spec.rb +30 -0
  23. data/spec/d2l/valence/host_spec.rb +64 -0
  24. data/spec/d2l/valence/request/authenticated_uri_spec.rb +75 -0
  25. data/spec/d2l/valence/request/execute/invalid_time_stamp_spec.rb +40 -0
  26. data/spec/d2l/valence/request/execute/lti_links/create_spec.rb +66 -0
  27. data/spec/d2l/valence/request/execute/lti_links/delete_spec.rb +39 -0
  28. data/spec/d2l/valence/request/execute/lti_links/list_spec.rb +41 -0
  29. data/spec/d2l/valence/request/execute/lti_links/update_spec.rb +73 -0
  30. data/spec/d2l/valence/request/execute/lti_links/view_spec.rb +41 -0
  31. data/spec/d2l/valence/request/execute/version_spec.rb +41 -0
  32. data/spec/d2l/valence/request/execute/whoami_spec.rb +40 -0
  33. data/spec/d2l/valence/response/code_spec.rb +34 -0
  34. data/spec/d2l/valence/response/server_skew_spec.rb +33 -0
  35. data/spec/d2l/valence/timestamp_error_spec.rb +21 -0
  36. data/spec/d2l/valence/user_context_spec.rb +6 -0
  37. data/spec/fixtures/vcr_cassettes/request/execute/create_lti_link.yml +71 -0
  38. data/spec/fixtures/vcr_cassettes/request/execute/delete_lti_link.yml +48 -0
  39. data/spec/fixtures/vcr_cassettes/request/execute/get_a_lti_link.yml +66 -0
  40. data/spec/fixtures/vcr_cassettes/request/execute/get_lti_links.yml +70 -0
  41. data/spec/fixtures/vcr_cassettes/request/execute/get_version.yml +66 -0
  42. data/spec/fixtures/vcr_cassettes/request/execute/get_whoami.yml +61 -0
  43. data/spec/fixtures/vcr_cassettes/request/execute/invalid_timestamp.yml +112 -0
  44. data/spec/fixtures/vcr_cassettes/request/execute/put_lti_link.yml +139 -0
  45. data/spec/spec_helper.rb +20 -0
  46. data/spec/support/common_context.rb +37 -0
  47. metadata +228 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 54c02bc20fd56e2ae54c623a642d0b6f8f83d493
4
+ data.tar.gz: d55700ebfc5b739baf6dcf79e08059450f5d2946
5
+ SHA512:
6
+ metadata.gz: f99a543b75d5da9219d4a27ce3a45b6c3fdad283ddb86e84f942e6cd366731b16bd80dce18b2e739b150f6df26622b14fd76a44861da404ae19774df2bbda8a9
7
+ data.tar.gz: 4d7a8d8d88a0b406af1967252b13fca056afb3b1173b342742f7bd62652885be6e1f086be1ad38e46c613148c75934d6d7d6efffebdc025e8887bef0ee7c2253
@@ -0,0 +1,22 @@
1
+ ---
2
+ engines:
3
+ bundler-audit:
4
+ enabled: true
5
+ duplication:
6
+ enabled: true
7
+ config:
8
+ languages:
9
+ - ruby
10
+ fixme:
11
+ enabled: true
12
+ rubocop:
13
+ enabled: true
14
+ ratings:
15
+ paths:
16
+ - Gemfile.lock
17
+ - "lib/**/*"
18
+ exclude_paths:
19
+ - spec/
20
+ - .idea/
21
+ - sandpit/
22
+
@@ -0,0 +1,17 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.bundle
10
+ *.so
11
+ *.o
12
+ *.a
13
+ mkmf.log
14
+ /.idea
15
+ .ruby-version
16
+ .ruby-gemset
17
+ /sandpit
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ Include:
3
+ - rubocop-rspec
4
+ Exclude:
5
+ - 'spec/**/*'
6
+ - '.idea/**/*'
7
+ - 'sandpit/**/*'
8
+ - 'Rakefile'
9
+ TargetRubyVersion: 2.3
10
+ Metrics/LineLength:
11
+ Max: 120
12
+ Style/FrozenStringLiteralComment:
13
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in d2l-valence.gemspec
4
+ gemspec
@@ -0,0 +1,71 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ d2l-valence (0.0.1)
5
+ rest-client (~> 2.0.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ addressable (2.5.0)
11
+ public_suffix (~> 2.0, >= 2.0.2)
12
+ crack (0.4.3)
13
+ safe_yaml (~> 1.0.0)
14
+ diff-lcs (1.3)
15
+ domain_name (0.5.20170223)
16
+ unf (>= 0.0.5, < 1.0.0)
17
+ hashdiff (0.3.2)
18
+ http-cookie (1.0.3)
19
+ domain_name (~> 0.5)
20
+ mime-types (3.1)
21
+ mime-types-data (~> 3.2015)
22
+ mime-types-data (3.2016.0521)
23
+ netrc (0.11.0)
24
+ public_suffix (2.0.5)
25
+ rake (10.4.2)
26
+ rest-client (2.0.1)
27
+ http-cookie (>= 1.0.2, < 2.0)
28
+ mime-types (>= 1.16, < 4.0)
29
+ netrc (~> 0.8)
30
+ rspec (3.4.0)
31
+ rspec-core (~> 3.4.0)
32
+ rspec-expectations (~> 3.4.0)
33
+ rspec-mocks (~> 3.4.0)
34
+ rspec-core (3.4.4)
35
+ rspec-support (~> 3.4.0)
36
+ rspec-expectations (3.4.0)
37
+ diff-lcs (>= 1.2.0, < 2.0)
38
+ rspec-support (~> 3.4.0)
39
+ rspec-its (1.2.0)
40
+ rspec-core (>= 3.0.0)
41
+ rspec-expectations (>= 3.0.0)
42
+ rspec-mocks (3.4.1)
43
+ diff-lcs (>= 1.2.0, < 2.0)
44
+ rspec-support (~> 3.4.0)
45
+ rspec-support (3.4.1)
46
+ safe_yaml (1.0.4)
47
+ timecop (0.8.1)
48
+ unf (0.1.4)
49
+ unf_ext
50
+ unf_ext (0.0.7.2)
51
+ vcr (3.0.3)
52
+ webmock (2.0.3)
53
+ addressable (>= 2.3.6)
54
+ crack (>= 0.3.2)
55
+ hashdiff
56
+
57
+ PLATFORMS
58
+ ruby
59
+
60
+ DEPENDENCIES
61
+ bundler (~> 1.7)
62
+ d2l-valence!
63
+ rake (~> 10.0)
64
+ rspec (~> 3.4.0)
65
+ rspec-its (~> 1.2.0)
66
+ timecop (~> 0.8.1)
67
+ vcr (~> 3.0.0)
68
+ webmock (~> 2.0.0)
69
+
70
+ BUNDLED WITH
71
+ 1.14.3
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2017 Michael Harrison
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,266 @@
1
+ # D2l::Valence
2
+
3
+ This gem is aimed at providing a Ruby client for Desire2Learn's Valence Learning Framework APIs primarily
4
+ used for integration with D2L Brightspace
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'd2l-valence'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install d2l-valence
21
+
22
+ ## Usage
23
+
24
+ The first step is to get an API Key generated by your D2L Brightspace Admin. They will require you to provide a
25
+ `Trusted URL`. This is the URL in your application that processes the authorisation response from the D2L Brightspace
26
+ Server. If you do not have a hosted server for you application at the time of development then you can use the AP Test
27
+ Tool provided by D2L as the `Trusted URL`: https://apitesttool.desire2learnvalence.com/index.php
28
+
29
+ Once generated the key will include an `Application ID` and `Application Key` which is used in the authentication request.
30
+
31
+
32
+ ### Authentication URL
33
+ To start the process of authentication you will need to generate an Authentication URL.
34
+
35
+ ```ruby
36
+ app_context = D2L::Valence::AppContext.new(
37
+ brightspace_host: 'partners.brightspace.com',
38
+ app_id: 'l3LcL9Lvpyg-ViYNbK',
39
+ app_key: 'mz5HRx6ER6jLMqKRv6Fw',
40
+ api_version: '1.15' # optional defaults to 1.0
41
+ )
42
+
43
+ auth_url = app_context.auth_url('https://apitesttool.desire2learnvalence.com/index.php')
44
+ ```
45
+
46
+ Once you have generated your authentication URL you can do a redirect to it in your application if you have your
47
+ application hosted. If you application is not hosted as yet and you're using the D2L API Test Tool then just paste the
48
+ URL into your browser and the API Test Tool will appear. From there you'll be able to get the `User ID` and `User Key`
49
+ which will be used in all subsequent authenticated requests.
50
+
51
+ __NB:__ You will need to be authenticated on your instance of D2L Brightspace in order for the above function as expected.
52
+ The Valence API uses that account for all subsequent authenticated requests for authorisation so it's important that
53
+ you are authenticated with the right account.
54
+
55
+ ### Authenticated Requests
56
+ The D2L Valence API requires that all requests are signed with authentication details. This is based on your `App ID`
57
+ and `App Key` along with the `User ID` and `User Key` that you harvest from the authentication response from your D2L
58
+ Server.
59
+
60
+ #### User context creation
61
+ In order to make requests against the D2L Valence API you will need to create a `D2L::Valence::UserContext` instance. This
62
+ will then be used for all of your subsequent requests against the various D2L Valence APIs. Following is an example of
63
+ the user context creation when processing the D2L Authentication response.
64
+
65
+ ```ruby
66
+ app_context = D2L::Valence::AppContext.new(
67
+ brightspace_host: 'partners.brightspace.com',
68
+ app_id: 'l3LcL9Lvpyg-ViYNbK',
69
+ app_key: 'mz5HRx6ER6jLMqKRv6Fw',
70
+ api_version: '1.15' # optional defaults to 1.0
71
+ )
72
+
73
+ user_id = params['x_a'] # query parameters from the response
74
+ user_key = params['x_b']
75
+
76
+ user_context = D2L::Valence::UserContext.new(
77
+ app_context: app_context,
78
+ user_id: user_id,
79
+ user_key: user_key
80
+ )
81
+
82
+ ```
83
+
84
+ Please note if you're using a application framework that supports sessions (e.g. Rails, Sinatra, etc) then it's probably
85
+ best store the `User ID` and `User Key` in the session to allow for multiple API requests.
86
+
87
+
88
+ #### Route Format
89
+ When creating your request you will need to specify a `rout`. The format of the `route` provided when doing a request
90
+ allows you to specify parameters that are replaced based on the `route_params` hash that you provide in the request
91
+ creation. The only parameter that's pre-populated is the `version` which is based on the api_version you supply when
92
+ creating your application context. It can, however be overridden when you supply your `route_params`. Following are
93
+ some examples of `route` and `route_params`
94
+
95
+ ```ruby
96
+
97
+ # NB: For the following example the api_version in the app_context has been set to 1.0
98
+
99
+ route = '/d2l/api/lp/:version/:orgUnitId/groupcategories/:groupCategoryId'
100
+ route_params = {orgUnitId: 1, groupCategoryId: 23} # => /d2l/api/lp/1.0/1/groupcategories/23
101
+
102
+ route = '/d2l/api/lp/:version/users/whoami'
103
+ route_params = {version: '1.15'} # => /d2l/api/lp/1.15/users/whoami
104
+
105
+ ```
106
+
107
+ ### Request Examples
108
+
109
+ #### GET Request example
110
+
111
+ [GET /d2l/api/lp/(version)/users/whoami](http://docs.valence.desire2learn.com/res/user.html#get--d2l-api-lp-(version)-users-whoami)
112
+
113
+ ```ruby
114
+ response = D2L::Valence::Request.new(
115
+ user_context: user_context,
116
+ http_method: 'GET',
117
+ route: '/d2l/api/lp/:version/users/whoami'
118
+ ).execute
119
+
120
+ response.code # => :HTTP_200
121
+ response.to_hash # => will yield the following hash
122
+ {
123
+ "Identifier" => "1",
124
+ "FirstName" => "Jack",
125
+ "LastName" => "User",
126
+ "UniqueName" => "jack.user",
127
+ "ProfileIdentifier" => "OqW9594ZXT"
128
+ }
129
+ ```
130
+
131
+ #### POST Request example
132
+ [POST /d2l/api/le/(version)/lti/link/(orgUnitId)¶](http://docs.valence.desire2learn.com/res/lti.html#post--d2l-api-le-(version)-lti-link-(orgUnitId))
133
+
134
+ ```ruby
135
+
136
+ response = D2L::Valence::Request.new(
137
+ user_context: user_context,
138
+ http_method: 'POST',
139
+ route: '/d2l/api/le/:version/lti/link/:orgUnitId',
140
+ route_params: { orgUnitId: 123 },
141
+ query_params: {
142
+ Title: 'LTI Link',
143
+ Url: 'http://myapplication.com/tool/launch',
144
+ Description: 'Link for external tool',
145
+ Key: '2015141297208',
146
+ PlainSecret: 'a30be7c3550149b7a7daac3065f0e5e5',
147
+ IsVisible: false,
148
+ SignMessage: true,
149
+ SignWithTc: true,
150
+ SendTcInfo: true,
151
+ SendContextInfo: true,
152
+ SendUserId: true,
153
+ SendUserName: true,
154
+ SendUserEmail: true,
155
+ SendLinkTitle: true,
156
+ SendLinkDescription: true,
157
+ SendD2LUserName: true,
158
+ SendD2LOrgDefinedId: true,
159
+ SendD2LOrgRoleId: true,
160
+ UseToolProviderSecuritySettings: true,
161
+ CustomParameters: nil
162
+ }
163
+ ).execute
164
+
165
+ response.code # => :HTTP_200
166
+ response.to_hash # => full details of the created LTI Link with OrgUnitId and LtiLinkId included
167
+ ```
168
+
169
+ #### PUT Request example
170
+ [PUT /d2l/api/le/(version)/lti/link/(ltiLinkId)](http://docs.valence.desire2learn.com/res/lti.html#put--d2l-api-le-(version)-lti-link-(ltiLinkId))
171
+
172
+ ```ruby
173
+ response = D2L::Valence::Request.new(
174
+ user_context: user_context,
175
+ http_method: 'PUT',
176
+ route: '/d2l/api/le/:version/lti/link/:ltiLinkId',
177
+ route_params: { ltiLinkId: '123' },
178
+ query_params: { Title: 'New LTI Link Title' }
179
+ ).execute
180
+
181
+ response.code # => :HTTP_200
182
+ response.to_hash # => full details of the updated LTI Link with OrgUnitId and LtiLinkId included
183
+ ```
184
+
185
+ #### DELETE Request example
186
+ [DELETE /d2l/api/le/(version)/lti/link/(ltiLinkId)](http://docs.valence.desire2learn.com/res/lti.html#delete--d2l-api-le-(version)-lti-link-(ltiLinkId))
187
+
188
+ ```ruby
189
+ response = D2L::Valence::Request.new(
190
+ user_context: user_context,
191
+ http_method: 'DELETE',
192
+ route: '/d2l/api/le/:version/lti/link/:ltiLinkId',
193
+ route_params: { ltiLinkId: '123' }
194
+ ).execute
195
+
196
+ response.code # => :HTTP_200
197
+ response.to_hash # => {}
198
+
199
+ ```
200
+
201
+ #### Request failures
202
+ Other than the normal HTTP response codes for failures there are a number of D2L Valence API specific response codes
203
+ that will appear with the right conditions.
204
+
205
+ ##### :INVALID_TIMESTAMP
206
+ This response code identifies that there is enough of a difference between your local server time and the D2L Server
207
+ time to be a problem. For developers this is hard to have changed so we provided a mechanism where by the failure can
208
+ be rectified at the application level. In the design of the gem there is detection of time skew between the client and
209
+ server. This is compensated for automatically. Should your request get this failure then all you need do is execute
210
+ your request a second time and the gem will take the skew into consideration when creating the necessary authentication
211
+ tokens. Following is an example of handling the response:
212
+
213
+ ```ruby
214
+ request = D2L::Valence::Request.new(
215
+ user_context: user_context,
216
+ http_method: 'GET',
217
+ route: '/d2l/api/lp/:version/users/whoami'
218
+ )
219
+
220
+ response = request.execute
221
+ response = request.execute if response.code == :INVALID_TIMESTAMP
222
+ response.code # => :HTTP_200
223
+
224
+ ```
225
+
226
+ ##### :INVALID_TOKEN
227
+ This response code occurs, as it suggests when you have provided an invalid set of authentication tokens. This could
228
+ be a combination of incorrect App ID/Key and/or User ID/Key.
229
+
230
+ ## Contributing
231
+
232
+ 1. Fork it ( https://github.com/michael-harrison/d2l-valence/fork )
233
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
234
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
235
+ 4. Push to the branch (`git push origin my-new-feature`)
236
+ 5. Create a new Pull Request
237
+
238
+ ### Testing notes
239
+ Given the use of timestamps in the the URL it does mean that any tests that have recorded HTTP traffic via [VCR](https://github.com/vcr/vcr)
240
+ need to use [Timecop](https://github.com/travisjeffery/timecop). In some cases the calls will need to wrapped with a
241
+ Timecop block:
242
+
243
+ ```ruby
244
+ Timecop.freeze Time.at(1491780043) do
245
+ # Your HTTP Calls
246
+ end
247
+ ```
248
+
249
+ But for the majority of the tests it just a matter of adding a `before` and `after`:
250
+
251
+ ```ruby
252
+ before { Timecop.freeze Time.at(1491547536) }
253
+ after { Timecop.return }
254
+ ```
255
+
256
+ In order to do PRs with passing tests you'll need to use your own temporary App ID, App Key, User ID and User Key as I've
257
+ done with these tests. For convenience the tests uses environment variables:
258
+
259
+ ```ruby
260
+ let(:app_id) { ENV['D2L_API_ID'] }
261
+ let(:app_key) { ENV['D2L_API_KEY'] }
262
+ let(:user_id) { ENV['D2L_USER_ID'] }
263
+ let(:user_key) { ENV['D2L_USER_KEY'] }
264
+ ```
265
+
266
+ It is a merry dance but the only reliable way to have real testing against a real API.
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'd2l/valence/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'd2l-valence'
8
+ spec.version = D2L::Valence::VERSION
9
+ spec.authors = ['Michael Harrison']
10
+ spec.email = ['michael@ereserve.com.au']
11
+ spec.summary = %w(D2L Valence Learning Framework API client for Ruby)
12
+ spec.description = %w(A Ruby client for Desire2Learn's Valence Learning Framework APIs primarily used for integration with D2L Brightspace)
13
+ spec.homepage = ''
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'rest-client', '~> 2.0.0'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.7'
24
+ spec.add_development_dependency 'rake', '~> 10.0'
25
+ spec.add_development_dependency 'rspec', '~> 3.4.0'
26
+ spec.add_development_dependency 'rspec-its', '~> 1.2.0'
27
+ spec.add_development_dependency 'vcr', '~> 3.0.0'
28
+ spec.add_development_dependency 'webmock', '~> 2.0.0'
29
+ spec.add_development_dependency 'timecop', '~> 0.8.1'
30
+ end