Skip to content

Commit be5984e

Browse files
committed
WIP: Improve student creation
1 parent 78d5a13 commit be5984e

File tree

4 files changed

+64
-23
lines changed

4 files changed

+64
-23
lines changed

lib/concepts/school_student/create.rb

+7-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ def call(school:, school_student_params:, token:)
99
response
1010
rescue StandardError => e
1111
Sentry.capture_exception(e)
12-
response[:error] = "Error creating school student: #{e}"
12+
13+
response[:error] = "Error creating school student: "
14+
if e.is_a?(ProfileApiClient::CreateStudentError) && e.duplicate_username?
15+
response[:error] += "username has already been taken"
16+
else
17+
response[:error] += e.to_s
18+
end
1319
response
1420
end
1521

lib/profile_api_client.rb

+15-7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,20 @@ class ProfileApiClient
66
owner: 'school:owner'
77
}.freeze
88

9+
class Error < StandardError; end
10+
11+
class CreateStudentError < Error
12+
def initialize(response)
13+
@status_code = response.status
14+
@body = response.body
15+
super "Student not created in Profile API (status code #{@status_code})"
16+
end
17+
18+
def duplicate_username?
19+
(@body['errors']&.first || {})['error'] == 'ERR_USER_EXISTS'
20+
end
21+
end
22+
923
class << self
1024
# TODO: Replace with HTTP requests once the profile API has been built.
1125

@@ -158,13 +172,7 @@ def create_school_student(token:, username:, password:, name:, school_id:)
158172
}]
159173
end
160174

161-
if response.status == 422
162-
error_code = JSON.parse(response.body)['errors'].first['error']
163-
message = error_code == 'ERR_USER_EXISTS' ? 'Username already exists' : "Unknown error code: #{error_code}"
164-
raise "Student not created in Profile API (status code #{response.status}). #{message}"
165-
end
166-
167-
raise "Student not created in Profile API (status code #{response.status})" unless response.status == 201
175+
raise CreateStudentError.new(response) unless response.status == 201
168176

169177
response.body.deep_symbolize_keys
170178
end

spec/concepts/school_student/create_spec.rb

+13
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,17 @@
8484
expect(response[:error]).to match(/school is not verified/)
8585
end
8686
end
87+
88+
context 'when the student cannot be created in profile api' do
89+
let(:duplicate_username) { true }
90+
let(:exception) { instance_double(ProfileApiClient::CreateStudentError, duplicate_username?: duplicate_username) }
91+
92+
before do
93+
allow(ProfileApiClient).to receive(:create_school_student).and_raise(exception)
94+
end
95+
96+
it 'adds a useful error message' do
97+
# TODO
98+
end
99+
end
87100
end

spec/lib/profile_api_client_spec.rb

+29-15
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,34 @@
1212
allow(ENV).to receive(:fetch).with('PROFILE_API_KEY').and_return(api_key)
1313
end
1414

15+
describe ProfileApiClient::CreateStudentError do
16+
subject { described_class.new(response) }
17+
18+
let(:status) { '404' }
19+
let(:body) { '' }
20+
let(:response) { instance_double(Faraday::Response, status:, body:) }
21+
22+
it 'includes status code in the message' do
23+
expect(subject.message).to eq('Student not created in Profile API (status code 404)')
24+
end
25+
26+
describe '#duplicate_username?' do
27+
context 'when body includes duplicate username error code' do
28+
let(:body) { { 'errors' => [{ 'username' => 'jdoe', 'error' => 'ERR_USER_EXISTS' }] } }
29+
30+
it 'returns true' do
31+
expect(subject.duplicate_username?).to be(true)
32+
end
33+
end
34+
35+
context 'when body does not include duplicate username error code' do
36+
it 'returns false' do
37+
expect(subject.duplicate_username?).to be(false)
38+
end
39+
end
40+
end
41+
end
42+
1543
describe '.create_school' do
1644
let(:school) { build(:school, id: SecureRandom.uuid, code: SecureRandom.uuid) }
1745
let(:create_school_url) { "#{api_url}/api/v1/schools" }
@@ -331,25 +359,11 @@ def delete_safeguarding_flag
331359
expect(create_school_student).to eq(response)
332360
end
333361

334-
it 'raises exception with details of the error if 422 response indicates that the user already exists' do
335-
response = { 'errors' => [{ 'username' => 'jdoe', 'error' => 'ERR_USER_EXISTS' }] }
336-
stub_request(:post, create_students_url)
337-
.to_return(status: 422, body: response.to_json)
338-
expect { create_school_student }.to raise_error('Student not created in Profile API (status code 422). Username already exists')
339-
end
340-
341-
it 'raises exception including the error code if 422 response indicates that some other error occurred' do
342-
response = { 'errors' => [{ 'username' => 'jdoe', 'error' => 'ERR_UNKNOWN' }] }
343-
stub_request(:post, create_students_url)
344-
.to_return(status: 422, body: response.to_json)
345-
expect { create_school_student }.to raise_error('Student not created in Profile API (status code 422). Unknown error code: ERR_UNKNOWN')
346-
end
347-
348362
it 'raises exception if anything other than a 201 status code is returned' do
349363
stub_request(:post, create_students_url)
350364
.to_return(status: 200)
351365

352-
expect { create_school_student }.to raise_error(RuntimeError)
366+
expect { create_school_student }.to raise_error(ProfileApiClient::CreateStudentError)
353367
end
354368

355369
it 'includes details of underlying response when exception is raised' do

0 commit comments

Comments
 (0)