quiz_api_client 4.1.0 → 4.3.0

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -0
  3. data/lib/quiz_api_client/config.rb +29 -8
  4. data/lib/quiz_api_client/http_client.rb +11 -40
  5. data/lib/quiz_api_client/http_request/failure.rb +65 -0
  6. data/lib/quiz_api_client/http_request/metrics.rb +67 -0
  7. data/lib/quiz_api_client/services/items_service.rb +11 -0
  8. data/lib/quiz_api_client/services/quiz_clone_jobs_service.rb +11 -0
  9. data/lib/quiz_api_client/services/quiz_sync_job_service.rb +17 -0
  10. data/lib/quiz_api_client/services/quiz_sync_jobs_service.rb +16 -0
  11. data/lib/quiz_api_client/version.rb +1 -1
  12. data/lib/quiz_api_client.rb +12 -0
  13. data/spec/config_spec.rb +66 -0
  14. data/spec/contracts/interaction_types_service_spec.rb +22 -0
  15. data/spec/contracts/item_analyses_service_spec.rb +59 -0
  16. data/spec/contracts/items_service_spec.rb +59 -0
  17. data/spec/contracts/qti_imports_service_spec.rb +34 -0
  18. data/spec/contracts/quiz_clone_job_service_spec.rb +20 -0
  19. data/spec/contracts/quiz_clone_jobs_service_spec.rb +21 -0
  20. data/spec/contracts/quiz_entries_service_spec.rb +125 -0
  21. data/spec/contracts/quiz_service_spec.rb +68 -0
  22. data/spec/contracts/quiz_session_events_service_spec.rb +30 -0
  23. data/spec/contracts/quiz_session_result_service_spec.rb +42 -0
  24. data/spec/contracts/quiz_session_service_spec.rb +56 -0
  25. data/spec/contracts/quiz_sessions_service_spec.rb +28 -0
  26. data/spec/contracts/quiz_sync_job_service_spec.rb +21 -0
  27. data/spec/contracts/quiz_sync_jobs_service_spec.rb +21 -0
  28. data/spec/contracts/quizzes_service_spec.rb +80 -0
  29. data/spec/contracts/session_item_results_service_spec.rb +60 -0
  30. data/spec/contracts/session_items_service_spec.rb +21 -0
  31. data/spec/contracts/shared_banks_spec.rb +366 -0
  32. data/spec/contracts/shared_examples/http_delete_example.rb +56 -0
  33. data/spec/contracts/shared_examples/http_get_example.rb +139 -0
  34. data/spec/contracts/shared_examples/http_patch_example.rb +60 -0
  35. data/spec/contracts/shared_examples/http_post_example.rb +68 -0
  36. data/spec/contracts/shared_examples/http_put_example.rb +60 -0
  37. data/spec/http_client_spec.rb +347 -0
  38. data/spec/json_formatter_spec.rb +32 -0
  39. data/spec/quiz_api_client/http_request/failure_spec.rb +100 -0
  40. data/spec/quiz_api_client/http_request/metrics_spec.rb +75 -0
  41. data/spec/quiz_api_client_spec.rb +117 -0
  42. data/spec/services/base_api_service_spec.rb +50 -0
  43. data/spec/services/interaction_types_service_spec.rb +25 -0
  44. data/spec/services/item_analyses_service_spec.rb +76 -0
  45. data/spec/services/items_service_spec.rb +56 -0
  46. data/spec/services/jwt_service_spec.rb +66 -0
  47. data/spec/services/qti_imports_service_spec.rb +114 -0
  48. data/spec/services/quiz_analyses_service_spec.rb +44 -0
  49. data/spec/services/quiz_clone_job_service_spec.rb +41 -0
  50. data/spec/services/quiz_clone_jobs_service_spec.rb +77 -0
  51. data/spec/services/quiz_entries_service_spec.rb +71 -0
  52. data/spec/services/quiz_service_spec.rb +49 -0
  53. data/spec/services/quiz_session_events_service_spec.rb +42 -0
  54. data/spec/services/quiz_session_result_service_spec.rb +26 -0
  55. data/spec/services/quiz_session_service_spec.rb +49 -0
  56. data/spec/services/quiz_sessions_service_spec.rb +42 -0
  57. data/spec/services/quiz_sync_job_service_spec.rb +41 -0
  58. data/spec/services/quiz_sync_jobs_service_spec.rb +77 -0
  59. data/spec/services/quizzes_service_spec.rb +71 -0
  60. data/spec/services/session_item_results_service_spec.rb +33 -0
  61. data/spec/services/session_items_service_spec.rb +26 -0
  62. data/spec/spec_helper.rb +42 -0
  63. data/spec/support/pact_config.rb +64 -0
  64. data/spec/support/pact_helper.rb +19 -0
  65. metadata +130 -35
  66. data/.dockerignore +0 -7
  67. data/.editorconfig +0 -16
  68. data/.gitignore +0 -13
  69. data/.rspec +0 -3
  70. data/.rubocop.yml +0 -72
  71. data/CHANGELOG.md +0 -28
  72. data/Dockerfile +0 -12
  73. data/Gemfile +0 -5
  74. data/Jenkinsfile +0 -86
  75. data/bin/console +0 -7
  76. data/bin/contracts-generate +0 -26
  77. data/bin/setup +0 -65
  78. data/docker-compose.dev.override.yml +0 -11
  79. data/docker-compose.yml +0 -10
  80. data/quiz_api_client.gemspec +0 -59
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f901bb687e405ab11127e8cbbe476f38c3578d122d838b25438cc106287b4d83
4
- data.tar.gz: 6bae389bad7c912f709747a00a562e42cd10e4b85d1d72f5a7c5ffff6564ae61
3
+ metadata.gz: 11e55f74ad6fd90e5f09e214394d3d44103e7027d83ce03299f5670cbc79111d
4
+ data.tar.gz: 836308804ec45f08806c57bf63c6d7b9167e03eee52d955e7bca38b310f75305
5
5
  SHA512:
6
- metadata.gz: 519030cead9c1d820cf40bb489029433f751ce41e6488dcddfd7a8520ea2c47c5fa7a8a96e0aaea59640793f0efcc7fcd825c3d5e2fbe026533cb9d18208fd18
7
- data.tar.gz: b906985f27f6e78e734f248685ae071efde9414cc3198d5a214caf9d54d8c1d2f29e6cc7d9e53677eac2c1c1d4d9bbd3016f3cea210173c1cf00628784d18507
6
+ metadata.gz: 9ed1d073d41458ff170b5767eec5d37e560af8211f6fa5da0906d7d644411a9a4e589c7d644b807300dd76400374e0e9bcd58d4595bd2e4004d65d228e4afb9f
7
+ data.tar.gz: 26d1ce56705ad6a1ba18c93175ae572e4ab54e95438513095c663c6a3d6efda3a57cce29ff432ac6885b5bf139305f954bab53890360b89945fdc0c48878b123
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.
@@ -3,10 +3,13 @@ module QuizApiClient
3
3
  DEFAULT_ALLOWABLE_RESPONSE_CODES = [401, 422].freeze
4
4
  DEFAULT_PROTOCOL = 'https'.freeze
5
5
  ERROR_HANDLERS = %i[sentry_raven].freeze
6
+ METRICS_HANDLERS = %i[inststatsd].freeze
6
7
 
7
8
  class InvalidErrorHandler < StandardError; end
9
+ class InvalidMetricsHandler < StandardError; end
10
+ class InvalidMetricsNamespace < StandardError; end
8
11
 
9
- attr_reader :error_handler
12
+ attr_reader :error_handler, :metrics_handler, :metrics_namespace
10
13
  attr_writer :protocol, :allowable_response_codes
11
14
  attr_accessor :consumer_key, :consumer_request_id, :host, :shared_secret
12
15
 
@@ -19,9 +22,17 @@ module QuizApiClient
19
22
  end
20
23
 
21
24
  def error_handler=(handler)
22
- validate_error_hander!(handler)
25
+ validate_error_handler!(handler)
23
26
 
24
- @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
25
36
  end
26
37
 
27
38
  def allowable_response_codes
@@ -30,14 +41,24 @@ module QuizApiClient
30
41
 
31
42
  private
32
43
 
33
- def validate_error_hander!(handler)
44
+ def validate_error_handler!(handler)
34
45
  return unless handler
46
+ return if ERROR_HANDLERS.include?(handler)
35
47
 
36
- unless ERROR_HANDLERS.include?(handler)
37
- raise InvalidErrorHandler, "It must be one of the following: #{ERROR_HANDLERS.inspect}"
38
- end
48
+ raise InvalidErrorHandler, "It must be one of the following: #{ERROR_HANDLERS.inspect}"
49
+ end
39
50
 
40
- @error_handler = handler.to_sym
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'
41
62
  end
42
63
  end
43
64
  end
@@ -82,6 +82,7 @@ module QuizApiClient
82
82
  end
83
83
 
84
84
  def make_request(method, url, request_options = {})
85
+ start_time = Time.now
85
86
  resp = self.class.send(
86
87
  method,
87
88
  url,
@@ -91,6 +92,8 @@ module QuizApiClient
91
92
  resp
92
93
  rescue HTTParty::Error, Errno::ECONNREFUSED, Net::ReadTimeout => e
93
94
  raise_error(method, url, current_error: e)
95
+ ensure
96
+ record_metrics(method, url, resp, start_time, Time.now)
94
97
  end
95
98
 
96
99
  def make_paginated_request(method, url, options)
@@ -146,50 +149,18 @@ module QuizApiClient
146
149
  [url, options.merge(query: query)]
147
150
  end
148
151
 
149
- def generate_error_context(method, url, response = nil)
150
- context = {
151
- quiz_api_client: {
152
- request: {
153
- method: method,
154
- url: url
155
- }
156
- }
157
- }
158
-
159
- if response
160
- context[:quiz_api_client][:response] = {
161
- body: response.body,
162
- code: response.code
163
- }
164
- end
165
-
166
- context
167
- end
168
-
169
- def record_error_context(context)
170
- case config.error_handler
171
- when :sentry_raven
172
- require 'sentry-raven'
173
- Raven.extra_context(context)
174
- end
175
- end
152
+ def raise_error(method, url, response: nil, current_error: nil)
153
+ failure = QuizApiClient::HttpRequest::Failure.new(config)
176
154
 
177
- def error_message(current_error, url, response)
178
- if current_error && response
179
- "#{current_error.message}: #{url} responded #{response.body} (#{response.code})"
180
- elsif current_error
181
- current_error.message
182
- elsif response
183
- "#{url} responded #{response.body} (#{response.code})"
184
- end
155
+ failure.raise_error(method, url, response: response, current_error: current_error)
185
156
  end
186
157
 
187
- def raise_error(method, url, response: nil, current_error: nil, error_class: RequestFailed)
188
- context = generate_error_context(method, url, response)
189
-
190
- record_error_context(context)
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)
191
161
 
192
- raise error_class.new(context), error_message(current_error, url, response)
162
+ metrics.increment
163
+ metrics.duration(start_time, end_time)
193
164
  end
194
165
 
195
166
  def successful_response?(resp)
@@ -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(
@@ -4,6 +4,10 @@ module QuizApiClient::Services
4
4
  post_to_quiz_api(params: params, token: token)
5
5
  end
6
6
 
7
+ def create_batch(body:, token: nil)
8
+ batch_post_to_quiz_api(body: body, token: token)
9
+ end
10
+
7
11
  private
8
12
 
9
13
  def post_to_quiz_api(params:, token:)
@@ -11,5 +15,12 @@ module QuizApiClient::Services
11
15
  "/api/quizzes/#{params.fetch(:quiz_id)}/quiz_clone_jobs"
12
16
  )
13
17
  end
18
+
19
+ def batch_post_to_quiz_api(body:, token:)
20
+ client(token: token).post(
21
+ '/api/quiz_clone_jobs/create_batch',
22
+ body
23
+ )
24
+ end
14
25
  end
15
26
  end
@@ -0,0 +1,17 @@
1
+ module QuizApiClient::Services
2
+ class QuizSyncJobService < BaseApiService
3
+ def show(params:, token: nil)
4
+ raise 'Quiz Sync Job Id Required' unless params && params[:id]
5
+
6
+ get_from_quiz_api(params: params, token: token)
7
+ end
8
+
9
+ private
10
+
11
+ def get_from_quiz_api(params:, token:)
12
+ client(token: token).get(
13
+ "/api/quiz_sync_jobs/#{params.fetch(:id)}"
14
+ )
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module QuizApiClient::Services
2
+ class QuizSyncJobsService < BaseApiService
3
+ def create_batch(body:, token: nil)
4
+ batch_post_to_quiz_api(body: body, token: token)
5
+ end
6
+
7
+ private
8
+
9
+ def batch_post_to_quiz_api(body:, token:)
10
+ client(token: token).post(
11
+ '/api/quiz_sync_jobs/create_batch',
12
+ body
13
+ )
14
+ end
15
+ end
16
+ end
@@ -1,3 +1,3 @@
1
1
  module QuizApiClient
2
- VERSION = '4.1.0'.freeze
2
+ VERSION = '4.3.0'.freeze
3
3
  end
@@ -50,6 +50,14 @@ module QuizApiClient
50
50
  @_quiz_clone_jobs_service ||= Services::QuizCloneJobsService.new(config)
51
51
  end
52
52
 
53
+ def quiz_sync_job_service
54
+ @_quiz_sync_job_service ||= Services::QuizSyncJobService.new(config)
55
+ end
56
+
57
+ def quiz_sync_jobs_service
58
+ @_quiz_sync_jobs_service ||= Services::QuizSyncJobsService.new(config)
59
+ end
60
+
53
61
  def qti_imports_service
54
62
  @_qti_imports_service ||= Services::QtiImportsService.new(config)
55
63
  end
@@ -101,6 +109,8 @@ require 'quiz_api_client/version'
101
109
  require 'quiz_api_client/config'
102
110
  require 'quiz_api_client/http_client'
103
111
  require 'quiz_api_client/json_formatter'
112
+ require 'quiz_api_client/http_request/failure'
113
+ require 'quiz_api_client/http_request/metrics'
104
114
  require 'quiz_api_client/services/jwt_service'
105
115
  require 'quiz_api_client/services/base_api_service'
106
116
 
@@ -118,6 +128,8 @@ require 'quiz_api_client/services/quizzes_service'
118
128
  require 'quiz_api_client/services/quiz_session_result_service'
119
129
  require 'quiz_api_client/services/quiz_clone_job_service'
120
130
  require 'quiz_api_client/services/quiz_clone_jobs_service'
131
+ require 'quiz_api_client/services/quiz_sync_job_service'
132
+ require 'quiz_api_client/services/quiz_sync_jobs_service'
121
133
  require 'quiz_api_client/services/session_items_service'
122
134
  require 'quiz_api_client/services/session_item_results_service'
123
135
  require 'quiz_api_client/services/shared_banks'
@@ -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