Skip to content

Commit b8d52cb

Browse files
committed
WIP: Implement ProfileApiClient.list_school_students
1 parent 987ab48 commit b8d52cb

File tree

4 files changed

+100
-8
lines changed

4 files changed

+100
-8
lines changed

lib/concepts/school_student/list.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def call(school:, token:)
1616
private
1717

1818
def list_students(school, token)
19-
response = ProfileApiClient.list_school_students(token:, organisation_id: school.id)
19+
response = ProfileApiClient.list_school_students(token:, school:)
2020
user_ids = response.fetch(:ids)
2121

2222
User.from_userinfo(ids: user_ids)

lib/profile_api_client.rb

+13-6
Original file line numberDiff line numberDiff line change
@@ -128,16 +128,23 @@ def remove_school_teacher(token:, teacher_id:, organisation_id:)
128128
#
129129
# The API should respond:
130130
# - 422 Unprocessable if the constraints are not met
131-
def list_school_students(token:, organisation_id:)
131+
# rubocop:disable Metrics/AbcSize
132+
def list_school_students(token:, school:)
132133
return [] if token.blank?
133134

134-
_ = organisation_id
135+
response = connection.post("/api/v1/schools/#{school.id}/students/list") do |request|
136+
apply_default_headers(request, token)
137+
request.body = Role.student.where(school:).map(&:user_id).to_json
138+
end
135139

136-
# TODO: We should make Faraday raise a Ruby error for a non-2xx status
137-
# code so that SchoolOwner::Invite propagates the error in the response.
138-
response = { 'ids' => ['99999999-9999-9999-9999-999999999999'] }
139-
response.deep_symbolize_keys
140+
return [] if response.status == 404
141+
unless response.status == 200
142+
raise "Students cannot be listed in Profile API. HTTP response code: #{response.status}"
143+
end
144+
145+
JSON.parse(response.body).map(&:deep_symbolize_keys)
140146
end
147+
# rubocop:enable Metrics/AbcSize
141148

142149
# The API should enforce these constraints:
143150
# - The token has the school-owner or school-teacher role for the given organisation ID

spec/concepts/school_student/list_spec.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
described_class.call(school:, token:)
2222

2323
# TODO: Replace with WebMock assertion once the profile API has been built.
24-
expect(ProfileApiClient).to have_received(:list_school_students).with(token:, organisation_id: school.id)
24+
expect(ProfileApiClient).to have_received(:list_school_students).with(token:, school:)
2525
end
2626

2727
it 'returns the school students in the operation response' do

spec/lib/profile_api_client_spec.rb

+85
Original file line numberDiff line numberDiff line change
@@ -373,4 +373,89 @@ def create_school_student
373373
described_class.create_school_student(token:, username:, password:, name:, school:)
374374
end
375375
end
376+
377+
describe '.list_school_student' do
378+
let(:school) { build(:school, id: SecureRandom.uuid) }
379+
let(:list_students_url) { "#{api_url}/api/v1/schools/#{school.id}/students/list" }
380+
381+
before do
382+
stub_request(:post, list_students_url).to_return(status: 200, body: '[]')
383+
end
384+
385+
it 'makes a request to the profile api host' do
386+
list_school_students
387+
expect(WebMock).to have_requested(:post, list_students_url)
388+
end
389+
390+
it 'includes token in the authorization request header' do
391+
list_school_students
392+
expect(WebMock).to have_requested(:post, list_students_url).with(headers: { authorization: "Bearer #{token}" })
393+
end
394+
395+
it 'includes the profile api key in the x-api-key request header' do
396+
list_school_students
397+
expect(WebMock).to have_requested(:post, list_students_url).with(headers: { 'x-api-key' => api_key })
398+
end
399+
400+
it 'sets content-type of request to json' do
401+
list_school_students
402+
expect(WebMock).to have_requested(:post, list_students_url).with(headers: { 'content-type' => 'application/json' })
403+
end
404+
405+
it 'sets accept header to json' do
406+
list_school_students
407+
expect(WebMock).to have_requested(:post, list_students_url).with(headers: { 'accept' => 'application/json' })
408+
end
409+
410+
it 'sets body to the IDs of the students in the school' do
411+
student_role = Role.student.create!(school:, user_id: SecureRandom.uuid)
412+
Role.teacher.create!(school:, user_id: SecureRandom.uuid)
413+
Role.owner.create!(school:, user_id: SecureRandom.uuid)
414+
list_school_students
415+
expect(WebMock).to have_requested(:post, list_students_url).with(body: [student_role.user_id])
416+
end
417+
418+
# rubocop:disable RSpec/ExampleLength
419+
it 'returns a hash representing the student(s) if successful' do
420+
response = [
421+
{
422+
id: '549e4674-6ffd-4ac6-9a97-b4d7e5c0e5c5',
423+
schoolId: '132383f1-702a-46a0-9eb2-a40dd4f212e3',
424+
name: 'student-name',
425+
username: 'student-username',
426+
createdAt: '2024-07-03T13:00:40.041Z',
427+
updatedAt: '2024-07-03T13:00:40.041Z',
428+
discardedAt: nil
429+
}
430+
]
431+
stub_request(:post, list_students_url)
432+
.to_return(status: 200, body: response.to_json)
433+
expect(list_school_students).to eq(response)
434+
end
435+
# rubocop:enable RSpec/ExampleLength
436+
437+
it "returns empty array if the API returns a 404 because one or more of the student IDs aren't found" do
438+
stub_request(:post, list_students_url)
439+
.to_return(status: 404, body: '')
440+
expect(list_school_students).to eq([])
441+
end
442+
443+
it 'raises exception if anything other than a 200 status code is returned' do
444+
stub_request(:post, list_students_url)
445+
.to_return(status: 500)
446+
447+
expect { list_school_students }.to raise_error(RuntimeError)
448+
end
449+
450+
it 'includes details of underlying response when exception is raised' do
451+
stub_request(:post, list_students_url)
452+
.to_return(status: 401)
453+
454+
expect { list_school_students }.to raise_error('Students cannot be listed in Profile API. HTTP response code: 401')
455+
end
456+
457+
def list_school_students
458+
described_class.list_school_students(token:, school:)
459+
end
460+
end
376461
end

0 commit comments

Comments
 (0)