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.
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