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.
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