quiz_api_client 4.0.0 → 4.2.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 +4 -4
- data/README.md +4 -0
- data/lib/quiz_api_client/config.rb +35 -9
- data/lib/quiz_api_client/http_client.rb +24 -42
- data/lib/quiz_api_client/http_request/failure.rb +65 -0
- data/lib/quiz_api_client/http_request/metrics.rb +67 -0
- data/lib/quiz_api_client/services/items_service.rb +11 -0
- data/lib/quiz_api_client/version.rb +1 -1
- data/lib/quiz_api_client.rb +2 -0
- data/spec/config_spec.rb +66 -0
- data/spec/contracts/interaction_types_service_spec.rb +22 -0
- data/spec/contracts/item_analyses_service_spec.rb +59 -0
- data/spec/contracts/items_service_spec.rb +59 -0
- data/spec/contracts/qti_imports_service_spec.rb +34 -0
- data/spec/contracts/quiz_clone_job_service_spec.rb +20 -0
- data/spec/contracts/quiz_clone_jobs_service_spec.rb +21 -0
- data/spec/contracts/quiz_entries_service_spec.rb +125 -0
- data/spec/contracts/quiz_service_spec.rb +68 -0
- data/spec/contracts/quiz_session_events_service_spec.rb +30 -0
- data/spec/contracts/quiz_session_result_service_spec.rb +42 -0
- data/spec/contracts/quiz_session_service_spec.rb +56 -0
- data/spec/contracts/quiz_sessions_service_spec.rb +28 -0
- data/spec/contracts/quizzes_service_spec.rb +80 -0
- data/spec/contracts/session_item_results_service_spec.rb +60 -0
- data/spec/contracts/session_items_service_spec.rb +21 -0
- data/spec/contracts/shared_banks_spec.rb +366 -0
- data/spec/contracts/shared_examples/http_delete_example.rb +56 -0
- data/spec/contracts/shared_examples/http_get_example.rb +139 -0
- data/spec/contracts/shared_examples/http_patch_example.rb +60 -0
- data/spec/contracts/shared_examples/http_post_example.rb +60 -0
- data/spec/contracts/shared_examples/http_put_example.rb +60 -0
- data/spec/http_client_spec.rb +347 -0
- data/spec/json_formatter_spec.rb +32 -0
- data/spec/quiz_api_client/http_request/failure_spec.rb +100 -0
- data/spec/quiz_api_client/http_request/metrics_spec.rb +75 -0
- data/spec/quiz_api_client_spec.rb +117 -0
- data/spec/services/base_api_service_spec.rb +50 -0
- data/spec/services/interaction_types_service_spec.rb +25 -0
- data/spec/services/item_analyses_service_spec.rb +76 -0
- data/spec/services/items_service_spec.rb +56 -0
- data/spec/services/jwt_service_spec.rb +66 -0
- data/spec/services/qti_imports_service_spec.rb +114 -0
- data/spec/services/quiz_analyses_service_spec.rb +44 -0
- data/spec/services/quiz_clone_job_service_spec.rb +41 -0
- data/spec/services/quiz_clone_jobs_service_spec.rb +41 -0
- data/spec/services/quiz_entries_service_spec.rb +71 -0
- data/spec/services/quiz_service_spec.rb +49 -0
- data/spec/services/quiz_session_events_service_spec.rb +42 -0
- data/spec/services/quiz_session_result_service_spec.rb +26 -0
- data/spec/services/quiz_session_service_spec.rb +49 -0
- data/spec/services/quiz_sessions_service_spec.rb +42 -0
- data/spec/services/quizzes_service_spec.rb +71 -0
- data/spec/services/session_item_results_service_spec.rb +33 -0
- data/spec/services/session_items_service_spec.rb +26 -0
- data/spec/spec_helper.rb +42 -0
- data/spec/support/pact_config.rb +64 -0
- data/spec/support/pact_helper.rb +19 -0
- metadata +124 -38
- data/.dockerignore +0 -7
- data/.editorconfig +0 -16
- data/.gitignore +0 -13
- data/.rspec +0 -3
- data/.rubocop.yml +0 -72
- data/Dockerfile +0 -12
- data/Gemfile +0 -5
- data/Jenkinsfile +0 -86
- data/bin/console +0 -7
- data/bin/contracts-generate +0 -26
- data/bin/setup +0 -65
- data/docker-compose.dev.override.yml +0 -11
- data/docker-compose.yml +0 -10
- data/quiz_api_client.gemspec +0 -59
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e2f68d04f56475db3135d3b6f74e36d19a3300c213622c6829cad7066c7f77a2
|
|
4
|
+
data.tar.gz: ddea88d65b109c9b5c685c4c5f54c7de741d27fe236ce6164e6a6f5a01a180a1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4605af70971e585e3086828448d81903e180cacc99287003471f8c99863b940e719c5bb8735cf48c4bbf9aefb4486ab2ed72139c1d331b8fa8d5b73fdfab8a8f
|
|
7
|
+
data.tar.gz: f6fc9e7840d04d4ff65f5c442ccbc45b2314b565f801dc6a707c1ba8b13989e0aa57b43d9bd9cb39a947ad3eae08134e09cf82c9189a2d55a52c5bb0792b756f
|
data/README.md
CHANGED
|
@@ -41,6 +41,10 @@ client = QuizApiClient::Client.new(
|
|
|
41
41
|
|
|
42
42
|
In order to set addition error information in your error handling system, you can set the `error_hander` property of the config. The list of valid values can be found in the `QuizApiClient::Config::ERROR_HANDLERS` constant.
|
|
43
43
|
|
|
44
|
+
### Metrics
|
|
45
|
+
|
|
46
|
+
HTTP Request metrics will now be provided if the `setup_metrics` method is called. The list of valid values can be found in the `QuizApiClient::Config::METRICS_HANDLERS` constant. The count and duration metrics will be provided under the namespace provided.
|
|
47
|
+
|
|
44
48
|
### Creation of Tokens
|
|
45
49
|
|
|
46
50
|
JWTs are created without hitting quiz_api and and they are validated on quiz_api. Tokens are created for a given scope, expiration, and an optional resource_id.
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
module QuizApiClient
|
|
2
2
|
class Config
|
|
3
|
+
DEFAULT_ALLOWABLE_RESPONSE_CODES = [401, 422].freeze
|
|
3
4
|
DEFAULT_PROTOCOL = 'https'.freeze
|
|
4
5
|
ERROR_HANDLERS = %i[sentry_raven].freeze
|
|
6
|
+
METRICS_HANDLERS = %i[inststatsd].freeze
|
|
5
7
|
|
|
6
8
|
class InvalidErrorHandler < StandardError; end
|
|
9
|
+
class InvalidMetricsHandler < StandardError; end
|
|
10
|
+
class InvalidMetricsNamespace < StandardError; end
|
|
7
11
|
|
|
8
|
-
attr_reader :error_handler
|
|
9
|
-
attr_writer :protocol
|
|
12
|
+
attr_reader :error_handler, :metrics_handler, :metrics_namespace
|
|
13
|
+
attr_writer :protocol, :allowable_response_codes
|
|
10
14
|
attr_accessor :consumer_key, :consumer_request_id, :host, :shared_secret
|
|
11
15
|
|
|
12
16
|
def initialize
|
|
@@ -18,21 +22,43 @@ module QuizApiClient
|
|
|
18
22
|
end
|
|
19
23
|
|
|
20
24
|
def error_handler=(handler)
|
|
21
|
-
|
|
25
|
+
validate_error_handler!(handler)
|
|
22
26
|
|
|
23
|
-
@error_handler = handler
|
|
27
|
+
@error_handler = handler.to_sym
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def setup_metrics(handler, namespace)
|
|
31
|
+
validate_metrics_handler!(handler)
|
|
32
|
+
validate_metrics_namespace!(namespace)
|
|
33
|
+
|
|
34
|
+
@metrics_handler = handler.to_sym
|
|
35
|
+
@metrics_namespace = namespace.to_s.strip
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def allowable_response_codes
|
|
39
|
+
@allowable_response_codes || DEFAULT_ALLOWABLE_RESPONSE_CODES
|
|
24
40
|
end
|
|
25
41
|
|
|
26
42
|
private
|
|
27
43
|
|
|
28
|
-
def
|
|
44
|
+
def validate_error_handler!(handler)
|
|
29
45
|
return unless handler
|
|
46
|
+
return if ERROR_HANDLERS.include?(handler)
|
|
30
47
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
end
|
|
48
|
+
raise InvalidErrorHandler, "It must be one of the following: #{ERROR_HANDLERS.inspect}"
|
|
49
|
+
end
|
|
34
50
|
|
|
35
|
-
|
|
51
|
+
def validate_metrics_handler!(handler)
|
|
52
|
+
return unless handler
|
|
53
|
+
return if METRICS_HANDLERS.include?(handler)
|
|
54
|
+
|
|
55
|
+
raise InvalidMetricsHandler, "It must be one of the following: #{METRICS_HANDLERS.inspect}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def validate_metrics_namespace!(namespace)
|
|
59
|
+
return unless namespace.nil? || namespace.to_s.strip == ''
|
|
60
|
+
|
|
61
|
+
raise InvalidMetricsNamespace, 'It must be present'
|
|
36
62
|
end
|
|
37
63
|
end
|
|
38
64
|
end
|
|
@@ -4,7 +4,14 @@ module QuizApiClient
|
|
|
4
4
|
class HttpClient
|
|
5
5
|
include HTTParty
|
|
6
6
|
|
|
7
|
-
class RequestFailed < StandardError
|
|
7
|
+
class RequestFailed < StandardError
|
|
8
|
+
attr_reader :context
|
|
9
|
+
|
|
10
|
+
def initialize(context)
|
|
11
|
+
@context = context
|
|
12
|
+
super(context)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
8
15
|
|
|
9
16
|
attr_reader :jwt, :uri, :config
|
|
10
17
|
|
|
@@ -75,13 +82,18 @@ module QuizApiClient
|
|
|
75
82
|
end
|
|
76
83
|
|
|
77
84
|
def make_request(method, url, request_options = {})
|
|
78
|
-
|
|
85
|
+
start_time = Time.now
|
|
86
|
+
resp = self.class.send(
|
|
79
87
|
method,
|
|
80
88
|
url,
|
|
81
89
|
default_request_data.merge(request_options)
|
|
82
90
|
)
|
|
91
|
+
raise_error(method, url, response: resp) unless successful_response?(resp)
|
|
92
|
+
resp
|
|
83
93
|
rescue HTTParty::Error, Errno::ECONNREFUSED, Net::ReadTimeout => e
|
|
84
94
|
raise_error(method, url, current_error: e)
|
|
95
|
+
ensure
|
|
96
|
+
record_metrics(method, url, resp, start_time, Time.now)
|
|
85
97
|
end
|
|
86
98
|
|
|
87
99
|
def make_paginated_request(method, url, options)
|
|
@@ -91,8 +103,6 @@ module QuizApiClient
|
|
|
91
103
|
|
|
92
104
|
until request_url.nil?
|
|
93
105
|
resp = make_request(method, request_url, request_options)
|
|
94
|
-
raise_error(method, url, response: resp) unless resp.code == 200
|
|
95
|
-
|
|
96
106
|
entities.concat(resp.parsed_response)
|
|
97
107
|
request_url, request_options = next_page(resp, url, options)
|
|
98
108
|
end
|
|
@@ -139,50 +149,22 @@ module QuizApiClient
|
|
|
139
149
|
[url, options.merge(query: query)]
|
|
140
150
|
end
|
|
141
151
|
|
|
142
|
-
def
|
|
143
|
-
|
|
144
|
-
quiz_api_client: {
|
|
145
|
-
request: {
|
|
146
|
-
method: method,
|
|
147
|
-
url: url
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if response
|
|
153
|
-
context[:quiz_api_client][:response] = {
|
|
154
|
-
body: response.body,
|
|
155
|
-
code: response.code
|
|
156
|
-
}
|
|
157
|
-
end
|
|
152
|
+
def raise_error(method, url, response: nil, current_error: nil)
|
|
153
|
+
failure = QuizApiClient::HttpRequest::Failure.new(config)
|
|
158
154
|
|
|
159
|
-
|
|
155
|
+
failure.raise_error(method, url, response: response, current_error: current_error)
|
|
160
156
|
end
|
|
161
157
|
|
|
162
|
-
def
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
require 'sentry-raven'
|
|
166
|
-
Raven.extra_context(context)
|
|
167
|
-
end
|
|
168
|
-
end
|
|
158
|
+
def record_metrics(method, url, resp, start_time, end_time)
|
|
159
|
+
code = resp&.code || 0
|
|
160
|
+
metrics = QuizApiClient::HttpRequest::Metrics.new(config, method, url, code)
|
|
169
161
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
"#{current_error.message}: #{url} responded #{response.body} (#{response.code})"
|
|
173
|
-
elsif current_error
|
|
174
|
-
current_error.message
|
|
175
|
-
elsif response
|
|
176
|
-
"#{url} responded #{response.body} (#{response.code})"
|
|
177
|
-
end
|
|
162
|
+
metrics.increment
|
|
163
|
+
metrics.duration(start_time, end_time)
|
|
178
164
|
end
|
|
179
165
|
|
|
180
|
-
def
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
record_error_context(context)
|
|
184
|
-
|
|
185
|
-
raise error_class, error_message(current_error, url, response)
|
|
166
|
+
def successful_response?(resp)
|
|
167
|
+
resp.success? || config.allowable_response_codes.include?(resp.code)
|
|
186
168
|
end
|
|
187
169
|
end
|
|
188
170
|
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module QuizApiClient
|
|
2
|
+
module HttpRequest
|
|
3
|
+
class Failure
|
|
4
|
+
attr_reader :config
|
|
5
|
+
|
|
6
|
+
def initialize(config)
|
|
7
|
+
@config = config
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def raise_error(
|
|
11
|
+
method,
|
|
12
|
+
url,
|
|
13
|
+
response: nil,
|
|
14
|
+
current_error: nil,
|
|
15
|
+
error_class: QuizApiClient::HttpClient::RequestFailed
|
|
16
|
+
)
|
|
17
|
+
context = generate_error_context(method, url, response)
|
|
18
|
+
|
|
19
|
+
record_error_context(context)
|
|
20
|
+
|
|
21
|
+
raise error_class.new(context), error_message(current_error, url, response)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def generate_error_context(method, url, response = nil)
|
|
27
|
+
context = {
|
|
28
|
+
quiz_api_client: {
|
|
29
|
+
request: {
|
|
30
|
+
method: method,
|
|
31
|
+
url: url
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if response
|
|
37
|
+
context[:quiz_api_client][:response] = {
|
|
38
|
+
body: response.body,
|
|
39
|
+
code: response.code
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
context
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def record_error_context(context)
|
|
47
|
+
case config.error_handler
|
|
48
|
+
when :sentry_raven
|
|
49
|
+
require 'sentry-raven'
|
|
50
|
+
Raven.extra_context(context)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def error_message(current_error, url, response)
|
|
55
|
+
if current_error && response
|
|
56
|
+
"#{current_error.message}: #{url} responded #{response.body} (#{response.code})"
|
|
57
|
+
elsif current_error
|
|
58
|
+
current_error.message
|
|
59
|
+
elsif response
|
|
60
|
+
"#{url} responded #{response.body} (#{response.code})"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module QuizApiClient
|
|
2
|
+
module HttpRequest
|
|
3
|
+
class Metrics
|
|
4
|
+
extend Forwardable
|
|
5
|
+
|
|
6
|
+
attr_reader :config, :method, :url, :code
|
|
7
|
+
def_delegators :config, :metrics_handler, :metrics_namespace
|
|
8
|
+
|
|
9
|
+
def initialize(config, method, url, code)
|
|
10
|
+
@config = config
|
|
11
|
+
@method = method
|
|
12
|
+
@url = url
|
|
13
|
+
@code = code
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def increment
|
|
17
|
+
return unless configured?
|
|
18
|
+
|
|
19
|
+
case metrics_handler
|
|
20
|
+
when :inststatsd
|
|
21
|
+
InstStatsd::Statsd.increment count_metric_name, tags: tags
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def duration(start_time, end_time)
|
|
26
|
+
return unless configured?
|
|
27
|
+
|
|
28
|
+
duration_ms = ((end_time - start_time) * 1_000).round
|
|
29
|
+
|
|
30
|
+
case metrics_handler
|
|
31
|
+
when :inststatsd
|
|
32
|
+
InstStatsd::Statsd.timing duration_metric_name, duration_ms, tags: tags
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def configured?
|
|
39
|
+
metrics_handler_present? && metrics_namespace_present?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def count_metric_name
|
|
43
|
+
"#{metrics_namespace}.quiz_api_client.request.count"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def duration_metric_name
|
|
47
|
+
"#{metrics_namespace}.quiz_api_client.request.duration_ms"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def metrics_handler_present?
|
|
51
|
+
!metrics_handler.nil?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def metrics_namespace_present?
|
|
55
|
+
!metrics_namespace.nil?
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def tags
|
|
59
|
+
{
|
|
60
|
+
method: method,
|
|
61
|
+
status: code,
|
|
62
|
+
url: url
|
|
63
|
+
}
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
module QuizApiClient::Services
|
|
2
2
|
class ItemsService < BaseApiService
|
|
3
|
+
def list(params:, token: nil)
|
|
4
|
+
raise 'Quiz Id Required' unless params && params[:quiz_id]
|
|
5
|
+
|
|
6
|
+
list_from_quiz_api(params: params, token: token)
|
|
7
|
+
end
|
|
8
|
+
|
|
3
9
|
def create(params:, token: nil)
|
|
4
10
|
raise 'Quiz Id Required' unless params && params[:quiz_id]
|
|
5
11
|
|
|
@@ -8,6 +14,11 @@ module QuizApiClient::Services
|
|
|
8
14
|
|
|
9
15
|
private
|
|
10
16
|
|
|
17
|
+
def list_from_quiz_api(params:, token:)
|
|
18
|
+
quiz_id = params.delete(:quiz_id)
|
|
19
|
+
client(token: token).get("/api/quizzes/#{quiz_id}/items")
|
|
20
|
+
end
|
|
21
|
+
|
|
11
22
|
def post_to_quiz_api(params:, token:)
|
|
12
23
|
quiz_id = params.delete(:quiz_id)
|
|
13
24
|
client(token: token).post(
|
data/lib/quiz_api_client.rb
CHANGED
|
@@ -101,6 +101,8 @@ require 'quiz_api_client/version'
|
|
|
101
101
|
require 'quiz_api_client/config'
|
|
102
102
|
require 'quiz_api_client/http_client'
|
|
103
103
|
require 'quiz_api_client/json_formatter'
|
|
104
|
+
require 'quiz_api_client/http_request/failure'
|
|
105
|
+
require 'quiz_api_client/http_request/metrics'
|
|
104
106
|
require 'quiz_api_client/services/jwt_service'
|
|
105
107
|
require 'quiz_api_client/services/base_api_service'
|
|
106
108
|
|
data/spec/config_spec.rb
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
describe QuizApiClient::Config do
|
|
2
|
+
describe 'initialize with block' do
|
|
3
|
+
it 'sets the config value' do
|
|
4
|
+
config = described_class.new do |c|
|
|
5
|
+
c.host = 'a host'
|
|
6
|
+
end
|
|
7
|
+
expect(config.host).to eq 'a host'
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
describe '#protocol' do
|
|
12
|
+
it 'returns the default when nil' do
|
|
13
|
+
config = described_class.new
|
|
14
|
+
expect(config.protocol).to eq QuizApiClient::Config::DEFAULT_PROTOCOL
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe '#error_handler=' do
|
|
19
|
+
it 'raises an error with an invalid error handler' do
|
|
20
|
+
config = described_class.new
|
|
21
|
+
expect { config.error_handler = :invalid_error_handler }
|
|
22
|
+
.to raise_error(QuizApiClient::Config::InvalidErrorHandler)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe '#allowable_response_codes' do
|
|
27
|
+
it 'returns the default when nil' do
|
|
28
|
+
config = described_class.new
|
|
29
|
+
expect(config.allowable_response_codes).to eq QuizApiClient::Config::DEFAULT_ALLOWABLE_RESPONSE_CODES
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe '#setup_metrics' do
|
|
34
|
+
let(:valid_handler) { :inststatsd }
|
|
35
|
+
let(:invalid_handler) { :invalid_handler }
|
|
36
|
+
|
|
37
|
+
let(:valid_namespace) { :quiz_lti }
|
|
38
|
+
let(:invalid_namespace) { ' ' }
|
|
39
|
+
|
|
40
|
+
it 'raises an error with an invalid metrics handler' do
|
|
41
|
+
config = described_class.new
|
|
42
|
+
expect { config.setup_metrics(invalid_handler, valid_namespace) }
|
|
43
|
+
.to raise_error(QuizApiClient::Config::InvalidMetricsHandler)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'raises an error with an invalid namespace' do
|
|
47
|
+
config = described_class.new
|
|
48
|
+
expect { config.setup_metrics(valid_handler, invalid_namespace) }
|
|
49
|
+
.to raise_error(QuizApiClient::Config::InvalidMetricsNamespace)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'raises an error with an nil namespace' do
|
|
53
|
+
config = described_class.new
|
|
54
|
+
expect { config.setup_metrics(valid_handler, nil) }
|
|
55
|
+
.to raise_error(QuizApiClient::Config::InvalidMetricsNamespace)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'sets the handler and namespace successfully' do
|
|
59
|
+
config = described_class.new
|
|
60
|
+
config.setup_metrics(valid_handler, valid_namespace)
|
|
61
|
+
|
|
62
|
+
expect(config.metrics_handler).to eq valid_handler
|
|
63
|
+
expect(config.metrics_namespace).to eq valid_namespace.to_s
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
describe QuizApiClient::Services::InteractionTypesService, :pact do
|
|
2
|
+
include PactHelper
|
|
3
|
+
|
|
4
|
+
it_behaves_like 'a http get request to quiz_api collection endpoint' do
|
|
5
|
+
let(:provider_state) { 'a set of interaction types' }
|
|
6
|
+
let(:request_description) { 'a request to retrieve a list of interaction types' }
|
|
7
|
+
let(:quizzes_api_path) { '/api/interaction_types' }
|
|
8
|
+
let(:scope) { 'quiz.build' }
|
|
9
|
+
let(:params) { nil }
|
|
10
|
+
let(:response_body) do
|
|
11
|
+
Pact.each_like(
|
|
12
|
+
id: '1',
|
|
13
|
+
slug: 'choice',
|
|
14
|
+
name: 'Multiple Choice',
|
|
15
|
+
properties_schema: {},
|
|
16
|
+
scoring_algorithm_default: 'Equivalence',
|
|
17
|
+
user_response_type_options: ['Uuid']
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
let(:service_name) { :interaction_types_service }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
describe QuizApiClient::Services::ItemAnalysesService, :pact do
|
|
2
|
+
include PactHelper
|
|
3
|
+
|
|
4
|
+
it_behaves_like 'a http get request to quiz_api collection endpoint', testrail_id: %w[3402546] do
|
|
5
|
+
let(:request_description) { 'a request to retrieve item analyses all quiz_id items' }
|
|
6
|
+
let(:quiz_id) { 1 }
|
|
7
|
+
let(:quizzes_api_path) { "/api/quizzes/#{quiz_id}/stats/item_analyses" }
|
|
8
|
+
let(:provider_state) { 'quiz with item analysis' }
|
|
9
|
+
let(:resource_id) { '1' }
|
|
10
|
+
let(:scope) { 'quiz.analysis' }
|
|
11
|
+
let(:params) { { quiz_id: '1' } }
|
|
12
|
+
let(:response_body) do
|
|
13
|
+
[
|
|
14
|
+
{
|
|
15
|
+
item_id: Pact.like('1'),
|
|
16
|
+
points_possible: Pact.like(1.0),
|
|
17
|
+
item_body: Pact.like('item body'),
|
|
18
|
+
interaction_type: 'choice',
|
|
19
|
+
item_analysis: {},
|
|
20
|
+
batch_timestamp: Pact.like('2017-06-15T22:14:51.215Z'),
|
|
21
|
+
outcome_alignment_set_guid: Pact.like('cbedd9b0-d1ab-43a9-a3b2-89c3134a722a')
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
item_id: Pact.like('1'),
|
|
25
|
+
points_possible: Pact.like(1.0),
|
|
26
|
+
item_body: Pact.like('item body'),
|
|
27
|
+
interaction_type: 'choice',
|
|
28
|
+
item_analysis: {},
|
|
29
|
+
batch_timestamp: Pact.like('2017-06-15T22:14:51.215Z'),
|
|
30
|
+
outcome_alignment_set_guid: Pact.like('cbedd9b0-d1ab-43a9-a3b2-89c3134a722a')
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
end
|
|
34
|
+
let(:service_name) { :item_analyses_service }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it_behaves_like 'a http get only request to quiz_api', testrail_id: %w[3402547] do
|
|
38
|
+
let(:request_description) { 'a request to retrieve item analyses for a quiz item' }
|
|
39
|
+
let(:quiz_id) { 1 }
|
|
40
|
+
let(:id) { 1 }
|
|
41
|
+
let(:quizzes_api_path) { "/api/quizzes/#{quiz_id}/stats/item_analyses/#{id}" }
|
|
42
|
+
let(:provider_state) { 'quiz with item analysis' }
|
|
43
|
+
let(:resource_id) { '1' }
|
|
44
|
+
let(:scope) { 'quiz.analysis' }
|
|
45
|
+
let(:params) { { quiz_id: '1', id: '1' } }
|
|
46
|
+
let(:response_body) do
|
|
47
|
+
{
|
|
48
|
+
item_id: Pact.like('1'),
|
|
49
|
+
points_possible: Pact.like(1.0),
|
|
50
|
+
item_body: Pact.like('item body'),
|
|
51
|
+
interaction_type: 'choice',
|
|
52
|
+
item_analysis: {},
|
|
53
|
+
batch_timestamp: Pact.like('2017-06-15T22:14:51.215Z'),
|
|
54
|
+
outcome_alignment_set_guid: Pact.like('cbedd9b0-d1ab-43a9-a3b2-89c3134a722a')
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
let(:service_name) { :item_analyses_service }
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
describe QuizApiClient::Services::ItemsService, :pact do
|
|
2
|
+
include PactHelper
|
|
3
|
+
|
|
4
|
+
it_behaves_like 'a http post request to quiz_api' do
|
|
5
|
+
let(:provider_state) { 'a quiz and a multiple choice interaction type' }
|
|
6
|
+
let(:request_description) { 'a request to create an item' }
|
|
7
|
+
let(:quizzes_api_path) { "/api/quizzes/#{quiz_id}/items" }
|
|
8
|
+
let(:resource_id) { '1' }
|
|
9
|
+
let(:scope) { 'quiz.build' }
|
|
10
|
+
let(:quiz_id) { 1 }
|
|
11
|
+
let(:params) do
|
|
12
|
+
{
|
|
13
|
+
quiz_id: quiz_id,
|
|
14
|
+
interaction_data: {
|
|
15
|
+
choices: [{
|
|
16
|
+
id: 'guid',
|
|
17
|
+
itemBody: '<p>Choice</p>',
|
|
18
|
+
position: 1
|
|
19
|
+
}]
|
|
20
|
+
},
|
|
21
|
+
interaction_type_id: 1, # we'll seed InteractionType::MultipleChoice
|
|
22
|
+
item_body: 'Some kind of stem',
|
|
23
|
+
properties: {},
|
|
24
|
+
scoring_algorithm: 'Equivalence',
|
|
25
|
+
scoring_data: {
|
|
26
|
+
value: 'another guid'
|
|
27
|
+
},
|
|
28
|
+
title: 'A perfectly cromulent title',
|
|
29
|
+
user_response_type: 'Uuid'
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
let(:item_params) { params.clone.tap { |p| p.delete(:quiz_id) } }
|
|
33
|
+
let(:body) { { quiz_id: quiz_id, item: item_params } }
|
|
34
|
+
let(:response_body) do
|
|
35
|
+
{
|
|
36
|
+
id: Pact.like('1'),
|
|
37
|
+
interaction_data: {
|
|
38
|
+
choices: Pact.each_like(
|
|
39
|
+
id: 'a guid',
|
|
40
|
+
item_body: 'choice description',
|
|
41
|
+
position: 1
|
|
42
|
+
)
|
|
43
|
+
},
|
|
44
|
+
item_body: Pact.like(''),
|
|
45
|
+
properties: Pact.like({}),
|
|
46
|
+
status: Pact.like(''),
|
|
47
|
+
title: Pact.like(''),
|
|
48
|
+
user_response_type: Pact.like(''),
|
|
49
|
+
stimulus_id: Pact.like(''),
|
|
50
|
+
interaction_type: Pact.like({}),
|
|
51
|
+
scoring_data: {
|
|
52
|
+
value: Pact.like('')
|
|
53
|
+
},
|
|
54
|
+
scoring_algorithm: Pact.like('')
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
let(:service_name) { :items_service }
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
describe QuizApiClient::Services::QtiImportsService, :pact do
|
|
2
|
+
include PactHelper
|
|
3
|
+
|
|
4
|
+
it_behaves_like 'a http post request to quiz_api', testrail_id: %w[3402548] do
|
|
5
|
+
let(:request_description) { 'a request to import a QTI' }
|
|
6
|
+
let(:provider_state) { 'a quiz' }
|
|
7
|
+
let(:quiz_id) { 1 }
|
|
8
|
+
let(:url) { 'http://fake.url' }
|
|
9
|
+
let(:quizzes_api_path) { "/api/quizzes/#{quiz_id}/qti_imports" }
|
|
10
|
+
let(:scope) { 'qti_import.create' }
|
|
11
|
+
let(:resource_id) { quiz_id }
|
|
12
|
+
let(:params) do
|
|
13
|
+
{
|
|
14
|
+
quiz_id: quiz_id,
|
|
15
|
+
url: url
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
let(:body) { { qti_import: params } }
|
|
19
|
+
let(:response_body) do
|
|
20
|
+
{
|
|
21
|
+
id: Pact.like('1'),
|
|
22
|
+
parent_id: quiz_id.to_s,
|
|
23
|
+
parent_type: Pact.like('Quiz'),
|
|
24
|
+
status: 'scheduled',
|
|
25
|
+
url: url,
|
|
26
|
+
message: Pact.like(''),
|
|
27
|
+
created_at: Pact.like(''),
|
|
28
|
+
updated_at: Pact.like(''),
|
|
29
|
+
progress: Pact.like(0.0)
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
let(:service_name) { :qti_imports_service }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
describe QuizApiClient::Services::QuizCloneJobService, :pact do
|
|
2
|
+
include PactHelper
|
|
3
|
+
|
|
4
|
+
it_behaves_like 'a http get request to quiz_api' do
|
|
5
|
+
let(:request_description) { 'a request to retrieve a quiz clone job' }
|
|
6
|
+
let(:quizzes_api_path) { "/api/quiz_clone_jobs/#{resource_id}" }
|
|
7
|
+
let(:provider_state) { 'a quiz clone job' }
|
|
8
|
+
let(:scope) { 'quiz_clone_job.show' }
|
|
9
|
+
let(:params) { { id: resource_id } }
|
|
10
|
+
let(:resource_id) { '1' }
|
|
11
|
+
let(:response_body) do
|
|
12
|
+
{
|
|
13
|
+
id: Pact.like('1'),
|
|
14
|
+
original_quiz_id: Pact.like('1'),
|
|
15
|
+
status: Pact.like('scheduled')
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
let(:service_name) { :quiz_clone_job_service }
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
describe QuizApiClient::Services::QuizCloneJobsService, :pact do
|
|
2
|
+
include PactHelper
|
|
3
|
+
|
|
4
|
+
it_behaves_like 'a http post request to quiz_api' do
|
|
5
|
+
let(:request_description) { 'a request to clone a quiz' }
|
|
6
|
+
let(:quizzes_api_path) { "/api/quizzes/#{resource_id}/quiz_clone_jobs" }
|
|
7
|
+
let(:provider_state) { 'a quiz' }
|
|
8
|
+
let(:scope) { 'quiz.clone' }
|
|
9
|
+
let(:params) { { quiz_id: resource_id } }
|
|
10
|
+
let(:body) { {} }
|
|
11
|
+
let(:resource_id) { '1' }
|
|
12
|
+
let(:response_body) do
|
|
13
|
+
{
|
|
14
|
+
id: Pact.like('1'),
|
|
15
|
+
original_quiz_id: Pact.like('1'),
|
|
16
|
+
status: Pact.like('scheduled')
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
let(:service_name) { :quiz_clone_jobs_service }
|
|
20
|
+
end
|
|
21
|
+
end
|