quiz_api_client 4.1.0 → 4.3.0

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