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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -0
  3. data/lib/quiz_api_client/config.rb +35 -9
  4. data/lib/quiz_api_client/http_client.rb +24 -42
  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/version.rb +1 -1
  9. data/lib/quiz_api_client.rb +2 -0
  10. data/spec/config_spec.rb +66 -0
  11. data/spec/contracts/interaction_types_service_spec.rb +22 -0
  12. data/spec/contracts/item_analyses_service_spec.rb +59 -0
  13. data/spec/contracts/items_service_spec.rb +59 -0
  14. data/spec/contracts/qti_imports_service_spec.rb +34 -0
  15. data/spec/contracts/quiz_clone_job_service_spec.rb +20 -0
  16. data/spec/contracts/quiz_clone_jobs_service_spec.rb +21 -0
  17. data/spec/contracts/quiz_entries_service_spec.rb +125 -0
  18. data/spec/contracts/quiz_service_spec.rb +68 -0
  19. data/spec/contracts/quiz_session_events_service_spec.rb +30 -0
  20. data/spec/contracts/quiz_session_result_service_spec.rb +42 -0
  21. data/spec/contracts/quiz_session_service_spec.rb +56 -0
  22. data/spec/contracts/quiz_sessions_service_spec.rb +28 -0
  23. data/spec/contracts/quizzes_service_spec.rb +80 -0
  24. data/spec/contracts/session_item_results_service_spec.rb +60 -0
  25. data/spec/contracts/session_items_service_spec.rb +21 -0
  26. data/spec/contracts/shared_banks_spec.rb +366 -0
  27. data/spec/contracts/shared_examples/http_delete_example.rb +56 -0
  28. data/spec/contracts/shared_examples/http_get_example.rb +139 -0
  29. data/spec/contracts/shared_examples/http_patch_example.rb +60 -0
  30. data/spec/contracts/shared_examples/http_post_example.rb +60 -0
  31. data/spec/contracts/shared_examples/http_put_example.rb +60 -0
  32. data/spec/http_client_spec.rb +347 -0
  33. data/spec/json_formatter_spec.rb +32 -0
  34. data/spec/quiz_api_client/http_request/failure_spec.rb +100 -0
  35. data/spec/quiz_api_client/http_request/metrics_spec.rb +75 -0
  36. data/spec/quiz_api_client_spec.rb +117 -0
  37. data/spec/services/base_api_service_spec.rb +50 -0
  38. data/spec/services/interaction_types_service_spec.rb +25 -0
  39. data/spec/services/item_analyses_service_spec.rb +76 -0
  40. data/spec/services/items_service_spec.rb +56 -0
  41. data/spec/services/jwt_service_spec.rb +66 -0
  42. data/spec/services/qti_imports_service_spec.rb +114 -0
  43. data/spec/services/quiz_analyses_service_spec.rb +44 -0
  44. data/spec/services/quiz_clone_job_service_spec.rb +41 -0
  45. data/spec/services/quiz_clone_jobs_service_spec.rb +41 -0
  46. data/spec/services/quiz_entries_service_spec.rb +71 -0
  47. data/spec/services/quiz_service_spec.rb +49 -0
  48. data/spec/services/quiz_session_events_service_spec.rb +42 -0
  49. data/spec/services/quiz_session_result_service_spec.rb +26 -0
  50. data/spec/services/quiz_session_service_spec.rb +49 -0
  51. data/spec/services/quiz_sessions_service_spec.rb +42 -0
  52. data/spec/services/quizzes_service_spec.rb +71 -0
  53. data/spec/services/session_item_results_service_spec.rb +33 -0
  54. data/spec/services/session_items_service_spec.rb +26 -0
  55. data/spec/spec_helper.rb +42 -0
  56. data/spec/support/pact_config.rb +64 -0
  57. data/spec/support/pact_helper.rb +19 -0
  58. metadata +124 -38
  59. data/.dockerignore +0 -7
  60. data/.editorconfig +0 -16
  61. data/.gitignore +0 -13
  62. data/.rspec +0 -3
  63. data/.rubocop.yml +0 -72
  64. data/Dockerfile +0 -12
  65. data/Gemfile +0 -5
  66. data/Jenkinsfile +0 -86
  67. data/bin/console +0 -7
  68. data/bin/contracts-generate +0 -26
  69. data/bin/setup +0 -65
  70. data/docker-compose.dev.override.yml +0 -11
  71. data/docker-compose.yml +0 -10
  72. data/quiz_api_client.gemspec +0 -59
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48279a4ca7eeb3d7fc50e493733f5718c2960b2267d70db68b2956b0b11c99c1
4
- data.tar.gz: cbd66f799b72273b23b2e7a208fc10336bee5cf2cb93656987d0fdc5d1c173a7
3
+ metadata.gz: e2f68d04f56475db3135d3b6f74e36d19a3300c213622c6829cad7066c7f77a2
4
+ data.tar.gz: ddea88d65b109c9b5c685c4c5f54c7de741d27fe236ce6164e6a6f5a01a180a1
5
5
  SHA512:
6
- metadata.gz: d448acbf4ac471f2c8f55cda52191d35ade2d0e107546946e9cee8f6712556ac402160150555d8e3f978034843240deeeb4cec0a658a5fdf8ce2b2b8d3e1a452
7
- data.tar.gz: c73951b3bc89f5ff17b08ac1199aff03f3fd2a7e9cf701d5b2d9fffcc31d4bc76ddd2cc4384622584479ab32f2abf85abc3993554fb8a872a4b961eb8b23b4a3
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
- validate_error_hander!(handler)
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 validate_error_hander!(handler)
44
+ def validate_error_handler!(handler)
29
45
  return unless handler
46
+ return if ERROR_HANDLERS.include?(handler)
30
47
 
31
- unless ERROR_HANDLERS.include?(handler)
32
- raise InvalidErrorHandler, "It must be one of the following: #{ERROR_HANDLERS.inspect}"
33
- end
48
+ raise InvalidErrorHandler, "It must be one of the following: #{ERROR_HANDLERS.inspect}"
49
+ end
34
50
 
35
- @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'
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; end
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
- self.class.send(
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 generate_error_context(method, url, response = nil)
143
- context = {
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
- context
155
+ failure.raise_error(method, url, response: response, current_error: current_error)
160
156
  end
161
157
 
162
- def record_error_context(context)
163
- case config.error_handler
164
- when :sentry_raven
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
- def error_message(current_error, url, response)
171
- if current_error && response
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 raise_error(method, url, response: nil, current_error: nil, error_class: RequestFailed)
181
- context = generate_error_context(method, url, response)
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(
@@ -1,3 +1,3 @@
1
1
  module QuizApiClient
2
- VERSION = '4.0.0'.freeze
2
+ VERSION = '4.2.1'.freeze
3
3
  end
@@ -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
 
@@ -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