quiz_api_client 4.0.0 → 4.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|