learnosity-sdk 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +42 -0
- data/.gitignore +4 -1
- data/ChangeLog.md +16 -0
- data/Gemfile.lock +39 -0
- data/README.md +33 -8
- data/examples/lrn-sdk-rails/Gemfile +1 -1
- data/examples/lrn-sdk-rails/app/controllers/index_controller.rb +2 -1
- data/examples/simple/init_data.rb +11 -5
- data/examples/simple/init_items.rb +1 -0
- data/learnosity-sdk.gemspec +2 -1
- data/lib/learnosity/sdk/request/init.rb +237 -184
- data/lib/learnosity/sdk/utils.rb +1 -1
- data/lib/learnosity/sdk/version.rb +1 -1
- metadata +22 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba0bf92f67d01ee19c744c6aaeb8673d8837dc25
|
4
|
+
data.tar.gz: e3e37546a446b45dd1d2138dd558c7bc15d3522e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6687888f2a40215ad49d9197a0f3a4625ebb210bfa1cfafbe4987d7acf86aece7e3ce0889f487e8e4fdae82f7a6d8398f6a3b2907bd28dc998c786df44796dfd
|
7
|
+
data.tar.gz: 04cc99d5b8318d4c900887df355d245f089f4f348ae10603fff89363d5ab8d2d8a304c606f617998278d31cada4e9e2fc2ac31ee111e58025dbe319dd6cfd9b0
|
data/.codeclimate.yml
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
version: "2" # required to adjust maintainability checks
|
2
|
+
|
3
|
+
checks:
|
4
|
+
argument-count:
|
5
|
+
enabled: false
|
6
|
+
file-lines:
|
7
|
+
enabled: false
|
8
|
+
method-count:
|
9
|
+
enabled: false
|
10
|
+
method-lines:
|
11
|
+
enabled: false
|
12
|
+
similar-code:
|
13
|
+
config:
|
14
|
+
threshold: 150
|
15
|
+
identical-code:
|
16
|
+
config:
|
17
|
+
threshold: 100
|
18
|
+
method-complexity:
|
19
|
+
config:
|
20
|
+
threshold: 15
|
21
|
+
|
22
|
+
plugins:
|
23
|
+
# Ruby
|
24
|
+
bundler-audit:
|
25
|
+
enabled: true
|
26
|
+
rubocop:
|
27
|
+
enabled: true
|
28
|
+
duplication:
|
29
|
+
enabled: true
|
30
|
+
config:
|
31
|
+
languages:
|
32
|
+
- ruby:
|
33
|
+
|
34
|
+
# Other
|
35
|
+
git-legal:
|
36
|
+
enabled: true
|
37
|
+
fixme:
|
38
|
+
enabled: true
|
39
|
+
config:
|
40
|
+
strings:
|
41
|
+
- FIXME
|
42
|
+
- BUG
|
data/.gitignore
CHANGED
data/ChangeLog.md
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# Change Log
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
6
|
+
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [Unreleased]
|
9
|
+
|
10
|
+
## [v0.2.0] - 2019-08-12
|
11
|
+
### Added
|
12
|
+
- This ChangeLog!
|
13
|
+
- Telemetry data (basic information about the execution environment) is now added to the request objects being signed which is later read and logged internally by our APIs when the request is received. This allows us to better support our various SDKs and does not send any additional network requests. More information can be found in README.md.
|
14
|
+
|
15
|
+
## [v0.1.0] - 2017-05-10
|
16
|
+
Initial Release
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
learnosity-sdk (0.2.0)
|
5
|
+
sys-uname (~> 1.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
diff-lcs (1.3)
|
11
|
+
ffi (1.11.1)
|
12
|
+
rake (10.5.0)
|
13
|
+
rspec (3.8.0)
|
14
|
+
rspec-core (~> 3.8.0)
|
15
|
+
rspec-expectations (~> 3.8.0)
|
16
|
+
rspec-mocks (~> 3.8.0)
|
17
|
+
rspec-core (3.8.0)
|
18
|
+
rspec-support (~> 3.8.0)
|
19
|
+
rspec-expectations (3.8.3)
|
20
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
21
|
+
rspec-support (~> 3.8.0)
|
22
|
+
rspec-mocks (3.8.0)
|
23
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
24
|
+
rspec-support (~> 3.8.0)
|
25
|
+
rspec-support (3.8.0)
|
26
|
+
sys-uname (1.0.4)
|
27
|
+
ffi (>= 1.0.0)
|
28
|
+
|
29
|
+
PLATFORMS
|
30
|
+
ruby
|
31
|
+
|
32
|
+
DEPENDENCIES
|
33
|
+
bundler (>= 1.14)
|
34
|
+
learnosity-sdk!
|
35
|
+
rake (~> 10.0)
|
36
|
+
rspec (~> 3.0)
|
37
|
+
|
38
|
+
BUNDLED WITH
|
39
|
+
1.17.3
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Learnosity SDK - Ruby
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/learnosity-sdk.svg)](https://badge.fury.io/rb/learnosity-sdk)
|
4
|
+
|
3
5
|
This gem allows to ease integration with the following Learnosity APIs,
|
4
6
|
|
5
7
|
- Author API [author-api-doc]
|
@@ -26,7 +28,7 @@ You can build and install the SDK directly from a Git clone, with
|
|
26
28
|
cd learnosity-sdk-ruby/
|
27
29
|
bundle install
|
28
30
|
rake build
|
29
|
-
gem install --user-install pkg/learnosity-sdk-0.
|
31
|
+
gem install --user-install pkg/learnosity-sdk-0.2.0.gem
|
30
32
|
|
31
33
|
If `bundle` is missing, you can install it with
|
32
34
|
|
@@ -60,9 +62,11 @@ say, the `items` API, you just need to instantiate the
|
|
60
62
|
require "learnosity/sdk/request/init"
|
61
63
|
|
62
64
|
security_packet = {
|
63
|
-
|
64
|
-
|
65
|
+
# XXX: This is a Learnosity Demos consumer; replace it with your own consumer key
|
66
|
+
'consumer_key' => 'yis0TYCu7U9V4o7M',
|
67
|
+
'domain' => 'localhost'
|
65
68
|
}
|
69
|
+
# XXX: The consumer secret should be in a properly secured credential store, and *NEVER* checked in in revision control
|
66
70
|
consumer_secret = '74c5fd430cf1242a527f6223aebd42d30464be22'
|
67
71
|
items_request = { 'limit' => 50 }
|
68
72
|
|
@@ -109,9 +113,11 @@ require "learnosity/sdk/request/init"
|
|
109
113
|
|
110
114
|
|
111
115
|
security_packet = {
|
112
|
-
|
113
|
-
|
116
|
+
# XXX: This is a Learnosity Demos consumer; replace it with your own consumer key
|
117
|
+
'consumer_key' => 'yis0TYCu7U9V4o7M',
|
118
|
+
'domain' => 'localhost'
|
114
119
|
}
|
120
|
+
# XXX: The consumer secret should be in a properly secured credential store, and *NEVER* checked in in revision control
|
115
121
|
consumer_secret = '74c5fd430cf1242a527f6223aebd42d30464be22'
|
116
122
|
data_request = { 'limit' => 50 }
|
117
123
|
|
@@ -141,7 +147,13 @@ For the time being, you can iterate through pages by looping over the
|
|
141
147
|
`Init#new`/`Init#generate`/`Net::HTTP#post_form`, updating the `next` attribute
|
142
148
|
in the request.
|
143
149
|
|
144
|
-
|
150
|
+
```ruby
|
151
|
+
response = JSON.parse(res.body)
|
152
|
+
if ( !response['meta']['next'].nil? \
|
153
|
+
and !response['meta']['records'].nil? and response['meta']['records'] > 0)
|
154
|
+
data_request['next'] = response['meta']['next']
|
155
|
+
end
|
156
|
+
```
|
145
157
|
|
146
158
|
This will `require 'json'` to be able to parse the response.
|
147
159
|
|
@@ -190,6 +202,7 @@ require 'learnosity/sdk/request/init'
|
|
190
202
|
|
191
203
|
class IndexController < ApplicationController
|
192
204
|
@@security_packet = {
|
205
|
+
# XXX: This is a Learnosity Demos consumer; replace it with your own consumer key
|
193
206
|
'consumer_key' => 'yis0TYCu7U9V4o7M',
|
194
207
|
'domain' => 'localhost'
|
195
208
|
}
|
@@ -212,7 +225,7 @@ end
|
|
212
225
|
|
213
226
|
Add the HTML/Javascript boilerplate to the view, `app/views/index/index.html.erb`
|
214
227
|
|
215
|
-
```
|
228
|
+
```erb
|
216
229
|
<h1>Index#index</h1>
|
217
230
|
|
218
231
|
<div id="learnosity_assess"></div>
|
@@ -255,7 +268,19 @@ Just run
|
|
255
268
|
|
256
269
|
rake spec
|
257
270
|
|
258
|
-
to exercise the testsuite
|
271
|
+
to exercise the testsuite.
|
272
|
+
|
273
|
+
##Tracking
|
274
|
+
|
275
|
+
In version v0.2.0, we introduced code to track the following information by adding it to the request being signed:
|
276
|
+
|
277
|
+
SDK version
|
278
|
+
SDK language
|
279
|
+
SDK language version
|
280
|
+
Host platform (OS)
|
281
|
+
Platform version
|
282
|
+
|
283
|
+
We use this data to enable better support and feature planning. All subsequent versions of the SDK shall include this usage tracking.
|
259
284
|
|
260
285
|
|
261
286
|
[author-api-doc]: https://docs.learnosity.com/authoring/author
|
@@ -9,7 +9,7 @@ end
|
|
9
9
|
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
10
10
|
gem 'rails', '~> 5.0.2'
|
11
11
|
# Use sqlite3 as the database for Active Record
|
12
|
-
gem 'sqlite3'
|
12
|
+
gem 'sqlite3', '~> 1.3.6'
|
13
13
|
# Use Puma as the app server
|
14
14
|
gem 'puma', '~> 3.0'
|
15
15
|
# Use SCSS for stylesheets
|
@@ -3,6 +3,7 @@ require 'securerandom'
|
|
3
3
|
|
4
4
|
class IndexController < ApplicationController
|
5
5
|
@@security_packet = {
|
6
|
+
# XXX: This is a Learnosity Demos consumer; replace it with your own consumer key
|
6
7
|
'consumer_key' => 'yis0TYCu7U9V4o7M',
|
7
8
|
'domain' => 'localhost'
|
8
9
|
}
|
@@ -91,7 +92,7 @@ class IndexController < ApplicationController
|
|
91
92
|
"session_id" => SecureRandom.uuid,
|
92
93
|
"state" => "initial",
|
93
94
|
"type" => "submit_practice",
|
94
|
-
"user_id" => "
|
95
|
+
"user_id" => "$ANONYMIZED_USER_ID"
|
95
96
|
}
|
96
97
|
|
97
98
|
def index
|
@@ -4,7 +4,10 @@ require 'json'
|
|
4
4
|
|
5
5
|
require 'learnosity/sdk/request/init'
|
6
6
|
|
7
|
+
itembank_uri = URI('https://data.learnosity.com/v1/itembank/items')
|
8
|
+
|
7
9
|
security_packet = {
|
10
|
+
# XXX: This is a Learnosity Demos consumer; replace it with your own consumer key
|
8
11
|
'consumer_key' => 'yis0TYCu7U9V4o7M',
|
9
12
|
'domain' => 'localhost'
|
10
13
|
}
|
@@ -13,7 +16,7 @@ consumer_secret = '74c5fd430cf1242a527f6223aebd42d30464be22'
|
|
13
16
|
data_request = { 'limit' => 1 }
|
14
17
|
|
15
18
|
# Do 5 subsequent requests using the `next` pointer
|
16
|
-
[1,2,3,4,5].each
|
19
|
+
[1,2,3,4,5].each do |reqno|
|
17
20
|
init = Learnosity::Sdk::Request::Init.new(
|
18
21
|
'data',
|
19
22
|
security_packet,
|
@@ -23,12 +26,15 @@ data_request = { 'limit' => 1 }
|
|
23
26
|
|
24
27
|
request = init.generate
|
25
28
|
|
26
|
-
|
27
|
-
puts ">>> [#{itembankUri} (#{reqno})] #{JSON.generate(request)}"
|
29
|
+
puts ">>> [#{itembank_uri} (#{reqno})] #{JSON.generate(request)}"
|
28
30
|
|
29
|
-
res = Net::HTTP.post_form(
|
31
|
+
res = Net::HTTP.post_form(itembank_uri, request)
|
30
32
|
|
31
33
|
puts "<<< [#{res.code}] #{res.body}"
|
32
34
|
|
33
|
-
|
35
|
+
response = JSON.parse(res.body)
|
36
|
+
if !response['meta']['next'].nil? \
|
37
|
+
and !response['meta']['records'].nil? and response['meta']['records'] > 0
|
38
|
+
data_request['next'] = response['meta']['next']
|
39
|
+
end
|
34
40
|
end
|
data/learnosity-sdk.gemspec
CHANGED
@@ -30,7 +30,8 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
31
|
spec.require_paths = ["lib"]
|
32
32
|
|
33
|
-
spec.
|
33
|
+
spec.add_runtime_dependency "sys-uname", "~> 1.0"
|
34
|
+
spec.add_development_dependency "bundler", ">= 1.14"
|
34
35
|
spec.add_development_dependency "rake", "~> 10.0"
|
35
36
|
spec.add_development_dependency "rspec", "~> 3.0"
|
36
37
|
end
|
@@ -3,226 +3,279 @@ require 'json'
|
|
3
3
|
|
4
4
|
require 'learnosity/sdk/exceptions'
|
5
5
|
require 'learnosity/sdk/utils'
|
6
|
+
require 'learnosity/sdk/version'
|
7
|
+
require 'sys/uname'
|
6
8
|
|
7
9
|
module Learnosity
|
8
10
|
module Sdk
|
9
11
|
module Request
|
10
12
|
|
11
13
|
class Init
|
12
|
-
|
13
|
-
|
14
|
+
# XXX: Needs to be public for unit tests
|
15
|
+
attr_reader :security_packet, :request_string
|
14
16
|
|
15
17
|
# Keynames that are valid in the security_packet, they are also in
|
16
18
|
# the correct order for signature generation.
|
17
|
-
@@
|
19
|
+
@@valid_security_keys = ['consumer_key', 'domain', 'timestamp', 'expires', 'user_id'];
|
18
20
|
|
19
21
|
# Service names that are valid for `$service`
|
20
|
-
@@
|
22
|
+
@@valid_services = ['assess', 'author', 'data', 'events', 'items', 'questions', 'reports'];
|
23
|
+
|
24
|
+
# Determines if telemetry is enabled
|
25
|
+
@@telemetry_enabled = true
|
26
|
+
|
27
|
+
def self.enable_telemetry
|
28
|
+
@@telemetry_enabled = true
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.disable_telemetry
|
32
|
+
@@telemetry_enabled = false
|
33
|
+
end
|
21
34
|
|
22
35
|
def initialize(service, security_packet, secret, request_packet = nil, action = nil)
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
36
|
+
@sign_request_data = false
|
37
|
+
@service = service
|
38
|
+
@security_packet = security_packet.clone unless security_packet.nil?
|
39
|
+
@secret = secret
|
40
|
+
@request_packet = request_packet.clone unless request_packet.nil?
|
41
|
+
@action = action
|
42
|
+
|
43
|
+
validate
|
44
|
+
|
45
|
+
if @@telemetry_enabled
|
46
|
+
add_meta
|
47
|
+
end
|
48
|
+
|
49
|
+
set_service_options
|
50
|
+
|
51
|
+
@request_string = generate_request_string
|
52
|
+
@security_packet['signature'] = generate_signature
|
35
53
|
end
|
36
54
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
55
|
+
def generate_signature
|
56
|
+
signature_array = []
|
57
|
+
@@valid_security_keys.each do |k|
|
58
|
+
if @security_packet.include? k
|
59
|
+
signature_array.<< @security_packet[k]
|
60
|
+
end
|
61
|
+
end
|
44
62
|
|
45
|
-
|
63
|
+
signature_array << @secret
|
46
64
|
|
47
|
-
|
48
|
-
|
49
|
-
|
65
|
+
if @sign_request_data and ! @request_string.nil?
|
66
|
+
signature_array << @request_string
|
67
|
+
end
|
50
68
|
|
51
|
-
|
52
|
-
|
53
|
-
|
69
|
+
unless @action.nil?
|
70
|
+
signature_array << @action
|
71
|
+
end
|
54
72
|
|
55
|
-
|
56
|
-
|
73
|
+
hash_signature(signature_array)
|
74
|
+
end
|
57
75
|
|
58
76
|
def generate(encode = true)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
if output.key?('request')
|
73
|
-
dataOutput['request'] = JSON.generate(output['request'])
|
74
|
-
end
|
75
|
-
if !@action.nil?
|
76
|
-
dataOutput['action'] = @action
|
77
|
-
end
|
78
|
-
return dataOutput
|
79
|
-
|
80
|
-
when 'assess'
|
81
|
-
output = output['request']
|
82
|
-
end
|
83
|
-
|
84
|
-
when 'questions'
|
85
|
-
output = hash_except(@security_packet, 'domain')
|
86
|
-
if !@request_packet.nil?
|
87
|
-
output = output.merge(@request_packet)
|
88
|
-
end
|
89
|
-
|
90
|
-
when 'events'
|
91
|
-
output['security'] = @security_packet
|
92
|
-
output['config'] = @request_packet
|
93
|
-
else
|
94
|
-
raise Exception, "generate() for #{@service} not implemented"
|
95
|
-
end
|
96
|
-
|
97
|
-
if !encode
|
98
|
-
return output
|
99
|
-
end
|
100
|
-
return JSON.generate(output)
|
101
|
-
end
|
77
|
+
output = {}
|
78
|
+
|
79
|
+
case @service
|
80
|
+
when 'assess', 'author', 'data', 'items', 'reports'
|
81
|
+
output['security'] = @security_packet
|
82
|
+
|
83
|
+
unless @request_packet.nil?
|
84
|
+
output['request'] = @request_packet
|
85
|
+
end
|
86
|
+
|
87
|
+
case @service
|
88
|
+
when 'data'
|
89
|
+
data_output = { 'security' => JSON.generate(output['security']) }
|
102
90
|
|
103
|
-
|
91
|
+
if output.key?('request')
|
92
|
+
data_output['request'] = JSON.generate(output['request'])
|
93
|
+
end
|
104
94
|
|
105
|
-
|
106
|
-
|
95
|
+
unless @action.nil?
|
96
|
+
data_output['action'] = @action
|
97
|
+
end
|
107
98
|
|
108
|
-
|
109
|
-
if @service.nil?
|
110
|
-
raise Learnosity::Sdk::ValidationException, 'The `service` argument wasn\'t found or was empty'
|
111
|
-
elsif ! @@validServices.include? @service
|
112
|
-
raise Learnosity::Sdk::ValidationException, "The service provided (#{service}) is not valid"
|
113
|
-
end
|
99
|
+
return data_output
|
114
100
|
|
115
|
-
|
101
|
+
when 'assess'
|
102
|
+
output = output['request']
|
103
|
+
end
|
116
104
|
|
117
|
-
|
118
|
-
|
119
|
-
else
|
120
|
-
@security_packet.each do |k, v|
|
121
|
-
if ! @@validSecurityKeys.include? k
|
122
|
-
raise ValidationException, "Invalid key found in the security packet: #{k}"
|
123
|
-
end
|
124
|
-
end
|
105
|
+
when 'questions'
|
106
|
+
output = hash_except(@security_packet, 'domain')
|
125
107
|
|
126
|
-
|
127
|
-
|
128
|
-
|
108
|
+
unless @request_packet.nil?
|
109
|
+
output = output.merge(@request_packet)
|
110
|
+
end
|
129
111
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
112
|
+
when 'events'
|
113
|
+
output['security'] = @security_packet
|
114
|
+
output['config'] = @request_packet
|
115
|
+
else
|
116
|
+
raise Exception, "generate() for #{@service} not implemented"
|
117
|
+
end
|
134
118
|
|
135
|
-
|
136
|
-
|
137
|
-
|
119
|
+
unless encode
|
120
|
+
return output
|
121
|
+
end
|
138
122
|
|
139
|
-
|
123
|
+
JSON.generate(output)
|
124
|
+
end
|
125
|
+
|
126
|
+
protected
|
127
|
+
|
128
|
+
attr_accessor :service, :secret, :request_packet, :action, :sign_request_data
|
129
|
+
attr_writer :security_packet, :request_string
|
130
|
+
|
131
|
+
def get_platform
|
132
|
+
if Sys::Platform.linux?
|
133
|
+
'linux'
|
134
|
+
elsif Sys::Platform.windows?
|
135
|
+
'win'
|
136
|
+
elsif Sys::Platform.mac?
|
137
|
+
'darwin'
|
138
|
+
else
|
139
|
+
Sys::Uname.platform
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def add_meta
|
144
|
+
if @request_packet.nil?
|
145
|
+
@request_packet = {}
|
146
|
+
end
|
147
|
+
|
148
|
+
sdk_metrics = {
|
149
|
+
:version => VERSION,
|
150
|
+
:lang => 'ruby',
|
151
|
+
:lang_version => RUBY_VERSION,
|
152
|
+
:platform => get_platform,
|
153
|
+
:platform_version => Sys::Uname.release
|
154
|
+
}
|
155
|
+
|
156
|
+
if @request_packet.include? 'meta'
|
157
|
+
@request_packet['meta'].delete('sdk') if @request_packet['meta'].include? 'sdk'
|
158
|
+
|
159
|
+
@request_packet['meta'][:sdk] = sdk_metrics
|
160
|
+
elsif @request_packet.include? :meta
|
161
|
+
@request_packet[:meta].delete('sdk') if @request_packet[:meta].include? 'sdk'
|
140
162
|
|
141
|
-
|
142
|
-
|
143
|
-
|
163
|
+
@request_packet[:meta][:sdk] = sdk_metrics
|
164
|
+
else
|
165
|
+
@request_packet[:meta] = {}
|
144
166
|
|
145
|
-
|
146
|
-
|
147
|
-
|
167
|
+
@request_packet[:meta][:sdk] = sdk_metrics
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def validate
|
172
|
+
if @service.nil?
|
173
|
+
raise Learnosity::Sdk::ValidationException, 'The `service` argument wasn\'t found or was empty'
|
174
|
+
elsif ! @@valid_services.include? @service
|
175
|
+
raise Learnosity::Sdk::ValidationException, "The service provided (#{service}) is not valid"
|
176
|
+
end
|
177
|
+
|
178
|
+
# XXX we don't do JSON to native object conversion for now, as the PHP SDK does
|
179
|
+
if @security_packet.nil? or ! @security_packet.is_a? Hash
|
180
|
+
raise Learnosity::Sdk::ValidationException, 'The security packet must be a Hash'
|
181
|
+
else
|
182
|
+
@security_packet.each do |k, v|
|
183
|
+
unless @@valid_security_keys.include? k
|
184
|
+
raise ValidationException, "Invalid key found in the security packet: #{k}"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
if @service == 'questions' and ! @security_packet.include? 'user_id'
|
189
|
+
raise ValidationException, 'Questions API requires a `user_id` in the security packet'
|
190
|
+
end
|
191
|
+
|
192
|
+
unless @security_packet.include? 'timestamp'
|
193
|
+
@security_packet['timestamp'] = Time.now.gmtime.strftime('%Y%m%d-%H%m')
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
if @secret.nil? or ! @secret.is_a? String
|
198
|
+
raise ValidationException, 'The `secret` argument must be a valid string'
|
199
|
+
end
|
200
|
+
|
201
|
+
# XXX we don't do JSON to native object conversion for now, as the PHP SDK does
|
202
|
+
if ! @request_packet.nil? and ! @request_packet.is_a? Hash
|
203
|
+
raise ValidationException, 'The request packet must be a hash'
|
204
|
+
end
|
205
|
+
|
206
|
+
if ! @action.nil? and ! @action.is_a? String
|
207
|
+
raise ValidationException, 'The `action` argument must be a string'
|
208
|
+
end
|
209
|
+
end
|
148
210
|
|
211
|
+
def set_service_options
|
212
|
+
case @service
|
213
|
+
when 'questions'
|
214
|
+
# nothing to do
|
215
|
+
when 'assess'
|
216
|
+
if @request_packet.key?('questionsApiActivity')
|
217
|
+
questions_api_activity = @request_packet['questionsApiActivity']
|
218
|
+
|
219
|
+
signature_parts = {}
|
220
|
+
signature_parts['consumer_key'] \
|
221
|
+
= questions_api_activity['consumer_key'] \
|
222
|
+
= @security_packet['consumer_key']
|
223
|
+
|
224
|
+
signature_parts['domain'] = @security_packet['domain'] \
|
225
|
+
|| questions_api_activity['domain'] \
|
226
|
+
|| 'assess.learnosity.com'
|
227
|
+
|
228
|
+
signature_parts['timestamp'] \
|
229
|
+
= questions_api_activity['timestamp'] \
|
230
|
+
= @security_packet['timestamp']
|
231
|
+
|
232
|
+
signature_parts['expires'] = \
|
233
|
+
questions_api_activity['expires'] \
|
234
|
+
= @security_packet['expires'] if @security_packet.key?('expires')
|
235
|
+
|
236
|
+
signature_parts['user_id'] = \
|
237
|
+
questions_api_activity['user_id'] = \
|
238
|
+
@security_packet['user_id']
|
239
|
+
|
240
|
+
signature_parts['secret'] = @security_packet['secret']
|
241
|
+
|
242
|
+
# Remove expires attribute if present but nil
|
243
|
+
questions_api_activity = hash_except(questions_api_activity, 'expires') if questions_api_activity['expires'].nil?
|
244
|
+
|
245
|
+
@security_packet = signature_parts
|
246
|
+
questions_api_activity['signature'] = generate_signature
|
247
|
+
@request_packet['questionsApiActivity'] = questions_api_activity
|
248
|
+
end
|
249
|
+
when 'items', 'reports'
|
250
|
+
@sign_request_data = true
|
251
|
+
if ! @request_packet.nil? and @request_packet.include? 'user_id' and
|
252
|
+
! @security_packet.include? 'user_id'
|
253
|
+
@security_packet['user_id'] = @request_packet['user_id']
|
254
|
+
end
|
255
|
+
when 'events'
|
256
|
+
hashed_users = {}
|
257
|
+
@request_packet['users'].each do |k, v|
|
258
|
+
hashed_users[k] = hash_value(k + @secret)
|
259
|
+
end
|
260
|
+
@request_packet['users'] = hashed_users
|
261
|
+
when 'author', 'data'
|
262
|
+
@sign_request_data = true
|
263
|
+
else
|
264
|
+
raise Exception, "set_service_options() for #{@service} not implemented"
|
265
|
+
end
|
149
266
|
end
|
150
267
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
# nothing to do
|
155
|
-
when 'assess'
|
156
|
-
if @request_packet.key?('questionsApiActivity')
|
157
|
-
questionsApiActivity = @request_packet['questionsApiActivity']
|
158
|
-
|
159
|
-
signatureParts = {}
|
160
|
-
signatureParts['consumer_key'] \
|
161
|
-
= questionsApiActivity['consumer_key'] \
|
162
|
-
= @security_packet['consumer_key']
|
163
|
-
|
164
|
-
signatureParts['domain'] = @security_packet['domain'] \
|
165
|
-
|| questionsApiActivity['domain'] \
|
166
|
-
|| 'assess.learnosity.com'
|
167
|
-
|
168
|
-
signatureParts['timestamp'] \
|
169
|
-
= questionsApiActivity['timestamp'] \
|
170
|
-
= @security_packet['timestamp']
|
171
|
-
|
172
|
-
signatureParts['expires'] = \
|
173
|
-
questionsApiActivity['expires'] \
|
174
|
-
= @security_packet['expires'] if @security_packet.key?('expires')
|
175
|
-
|
176
|
-
signatureParts['user_id'] = \
|
177
|
-
questionsApiActivity['user_id'] = \
|
178
|
-
@security_packet['user_id']
|
179
|
-
|
180
|
-
signatureParts['secret'] = @security_packet['secret']
|
181
|
-
|
182
|
-
# Remove expires attribute if present but nil
|
183
|
-
questionsApiActivity = hash_except(questionsApiActivity, 'expires') if questionsApiActivity['expires'].nil?
|
184
|
-
|
185
|
-
@security_packet = signatureParts
|
186
|
-
questionsApiActivity['signature'] = self.generate_signature()
|
187
|
-
@request_packet['questionsApiActivity'] = questionsApiActivity
|
188
|
-
|
189
|
-
end
|
190
|
-
when 'items', 'reports'
|
191
|
-
@sign_request_data = true
|
192
|
-
if ! @request_packet.nil? and @request_packet.include? 'user_id' and
|
193
|
-
! @security_packet.include? 'user_id'
|
194
|
-
@security_packet['user_id'] = @request_packet['user_id']
|
195
|
-
end
|
196
|
-
when 'events'
|
197
|
-
hashedUsers = {}
|
198
|
-
@request_packet['users'].each do |k, v|
|
199
|
-
hashedUsers[k] = self.hash_value(k + @secret)
|
200
|
-
end
|
201
|
-
@request_packet['users'] = hashedUsers
|
202
|
-
when 'author', 'data'
|
203
|
-
@sign_request_data = true
|
204
|
-
else
|
205
|
-
raise Exception, "set_service_options() for #{@service} not implemented"
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
def generate_request_string()
|
210
|
-
request_string = nil
|
211
|
-
if ! @request_packet.nil?
|
212
|
-
request_string = JSON.generate(@request_packet)
|
213
|
-
end
|
214
|
-
|
215
|
-
return request_string
|
216
|
-
end
|
217
|
-
|
218
|
-
def hash_value(value)
|
219
|
-
return Digest::SHA256.hexdigest value
|
220
|
-
end
|
221
|
-
|
222
|
-
def hash_signature(signature_array)
|
223
|
-
return self.hash_value(signature_array.join('_'))
|
224
|
-
end
|
268
|
+
def generate_request_string
|
269
|
+
JSON.generate @request_packet unless request_packet.nil?
|
270
|
+
end
|
225
271
|
|
272
|
+
def hash_value(value)
|
273
|
+
Digest::SHA256.hexdigest value
|
274
|
+
end
|
275
|
+
|
276
|
+
def hash_signature(signature_array)
|
277
|
+
hash_value(signature_array.join('_'))
|
278
|
+
end
|
226
279
|
end
|
227
280
|
|
228
281
|
end
|
data/lib/learnosity/sdk/utils.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: learnosity-sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Olivier Mehani
|
@@ -9,20 +9,34 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2019-08-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
15
|
+
name: sys-uname
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
18
|
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1.0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: bundler
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
19
33
|
- !ruby/object:Gem::Version
|
20
34
|
version: '1.14'
|
21
35
|
type: :development
|
22
36
|
prerelease: false
|
23
37
|
version_requirements: !ruby/object:Gem::Requirement
|
24
38
|
requirements:
|
25
|
-
- - "
|
39
|
+
- - ">="
|
26
40
|
- !ruby/object:Gem::Version
|
27
41
|
version: '1.14'
|
28
42
|
- !ruby/object:Gem::Dependency
|
@@ -61,10 +75,13 @@ executables: []
|
|
61
75
|
extensions: []
|
62
76
|
extra_rdoc_files: []
|
63
77
|
files:
|
78
|
+
- ".codeclimate.yml"
|
64
79
|
- ".gitignore"
|
65
80
|
- ".rspec"
|
66
81
|
- ".travis.yml"
|
82
|
+
- ChangeLog.md
|
67
83
|
- Gemfile
|
84
|
+
- Gemfile.lock
|
68
85
|
- LICENSE
|
69
86
|
- README.md
|
70
87
|
- Rakefile
|
@@ -180,7 +197,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
180
197
|
version: '0'
|
181
198
|
requirements: []
|
182
199
|
rubyforge_project:
|
183
|
-
rubygems_version: 2.
|
200
|
+
rubygems_version: 2.5.2.3
|
184
201
|
signing_key:
|
185
202
|
specification_version: 4
|
186
203
|
summary: SDK to interact with Learnosity APIs
|