Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions lib/profile_api_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class ProfileApiClient
}.freeze

# rubocop:disable Naming/MethodName
School = Data.define(:id, :schoolCode, :updatedAt, :createdAt, :discardedAt)
School = Data.define(:id, :schoolCode, :studentEmailDomains, :updatedAt, :createdAt, :discardedAt)
SafeguardingFlag = Data.define(:id, :userId, :schoolId, :flag, :email, :createdAt, :updatedAt, :discardedAt)
Student = Data.define(:id, :schoolId, :name, :username, :createdAt, :updatedAt, :discardedAt, :email, :ssoProviders)
# rubocop:enable Naming/MethodName
Expand Down Expand Up @@ -51,13 +51,14 @@ def check_auth(token:)
false
end

def create_school(token:, id:, code:)
return { 'id' => id, 'schoolCode' => code } if ENV['BYPASS_OAUTH'].present?
def create_school(token:, id:, code:, school_email_domains: [])
return { 'id' => id, 'schoolCode' => code, 'studentEmailDomains' => school_email_domains } if ENV['BYPASS_OAUTH'].present?

response = connection(token).post('/api/v1/schools') do |request|
request.body = {
id:,
schoolCode: code
schoolCode: code,
studentEmailDomains: school_email_domains
}
end

Expand Down Expand Up @@ -214,6 +215,21 @@ def delete_safeguarding_flag(token:, flag:)
raise UnexpectedResponse, response unless response.status == 204
end

def update_school_email_domains(token:, school_id:, school_email_domains: [])
return { 'id' => school_id, 'studentEmailDomains' => school_email_domains } if ENV['BYPASS_OAUTH'].present?

response = connection(token).patch("/api/v1/schools/#{school_id}") do |request|
request.body = {
studentEmailDomains: school_email_domains
}
end

unauthorized!(response)
raise UnexpectedResponse, response unless response.status == 200

School.new(**response.body)
end

private

def connection(token)
Expand Down
71 changes: 65 additions & 6 deletions spec/lib/profile_api_client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
stub_request(:post, create_school_url)
.to_return(
status: 201,
body: '{"id":"","schoolCode":"","updatedAt":"","createdAt":"","discardedAt":""}',
body: '{"id":"","schoolCode":"","updatedAt":"","createdAt":"","discardedAt":"","studentEmailDomains":[]}',
headers: { 'content-type' => 'application/json' }
)
end
Expand All @@ -67,14 +67,15 @@
it_behaves_like 'a request that handles standard HTTP errors', :post, url: -> { create_school_url }
it_behaves_like 'a request that handles an unexpected response status', :post, url: -> { "#{api_url}/api/v1/schools" }, status: 200

it 'sends the school id and code in the request body as json' do
it 'sends the school id, code and studentEmailDomains in the request body as json' do
create_school_response
expected_body = { id: school.id, schoolCode: school.code }.to_json
expected_body = { id: school.id, schoolCode: school.code, studentEmailDomains: [] }.to_json
Comment on lines +70 to +72
expect(WebMock).to have_requested(:post, create_school_url).with(body: expected_body)
end

it 'returns the created school if successful' do
data = { id: 'id', schoolCode: 'code', updatedAt: '2024-07-09T10:31:13.196Z', createdAt: '2024-07-09T10:31:13.196Z', discardedAt: nil }
data = { id: 'id', schoolCode: 'code', updatedAt: '2024-07-09T10:31:13.196Z', createdAt: '2024-07-09T10:31:13.196Z', discardedAt: nil,
studentEmailDomains: [] }
expected = ProfileApiClient::School.new(**data)
stub_request(:post, create_school_url)
.to_return(status: 201, body: data.to_json, headers: { 'Content-Type' => 'application/json' })
Expand All @@ -91,8 +92,8 @@
expect(WebMock).not_to have_requested(:post, create_school_url)
end

it 'returns the id and code of the school supplied' do
expected = { 'id' => school.id, 'schoolCode' => school.code }
it 'returns the id, code and email domains of the school supplied' do
expected = { 'id' => school.id, 'schoolCode' => school.code, 'studentEmailDomains' => [] }
expect(create_school_response).to eq(expected)
end
end
Expand Down Expand Up @@ -558,4 +559,62 @@ def school_student
described_class.school_student(token:, school_id: school.id, student_id:)
end
end

describe '.update_school_email_domains' do
subject(:update_school_email_domains_response) { update_school_email_domains }

let(:school) { build(:school, id: SecureRandom.uuid, code: SecureRandom.uuid) }
let(:update_school_email_domains_url) { "#{api_url}/api/v1/schools/#{school.id}" }
let(:school_email_domains) { ['student.example.edu', 'mail.example.org'] }

before do
stub_request(:patch, update_school_email_domains_url)
.to_return(
status: 200,
body: '{"id":"","schoolCode":"","updatedAt":"","createdAt":"","discardedAt":"","studentEmailDomains":[]}',
headers: { 'content-type' => 'application/json' }
)
end

it_behaves_like 'an authenticated JSON API request', :patch, url: -> { update_school_email_domains_url }
it_behaves_like 'a request that handles standard HTTP errors', :patch, url: -> { update_school_email_domains_url }
it_behaves_like 'a request that handles an unexpected response status', :patch, url: -> { "#{api_url}/api/v1/schools/#{school.id}" }, status: 201

it 'sends studentEmailDomains in the JSON body' do
update_school_email_domains_response
expected_body = { studentEmailDomains: school_email_domains }.to_json
expect(WebMock).to have_requested(:patch, update_school_email_domains_url).with(body: expected_body)
end

it 'returns a school if successful' do
data = { id: 'id', schoolCode: 'code', updatedAt: '2024-07-09T10:31:13.196Z', createdAt: '2024-07-09T10:31:13.196Z', discardedAt: nil,
studentEmailDomains: school_email_domains }
expected = ProfileApiClient::School.new(**data)
stub_request(:patch, update_school_email_domains_url)
.to_return(status: 200, body: data.to_json, headers: { 'Content-Type' => 'application/json' })
expect(update_school_email_domains_response).to eq(expected)
end

describe 'when BYPASS_OAUTH is true' do
before do
allow(ENV).to receive(:[]).with('BYPASS_OAUTH').and_return(true)
end

it 'does not make a request to Profile API' do
update_school_email_domains_response
expect(WebMock).not_to have_requested(:patch, update_school_email_domains_url)
end
Comment on lines +603 to +606
Copy link
Copy Markdown
Author

@PetarSimonovic PetarSimonovic May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using WebMock rather than expanding ProfileApiMock given the TODO comment at the top of that module


it 'returns the id and email domains of the school supplied' do
expected = { 'id' => school.id, 'studentEmailDomains' => school_email_domains }
expect(update_school_email_domains_response).to eq(expected)
end
end

private

def update_school_email_domains
described_class.update_school_email_domains(token:, school_id: school.id, school_email_domains:)
end
end
end
3 changes: 2 additions & 1 deletion spec/support/profile_api_mock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@ def stub_profile_api_create_school_student(user_id: SecureRandom.uuid)
allow(ProfileApiClient).to receive(:create_school_student).and_return(created: [user_id])
end

def stub_profile_api_create_school(id: SecureRandom.uuid, code: '99-12-34')
def stub_profile_api_create_school(id: SecureRandom.uuid, code: '99-12-34', student_email_domains: [])
now = Time.current.to_fs(:iso8601) # rubocop:disable Naming/VariableNumber
allow(ProfileApiClient).to receive(:create_school).and_return(
ProfileApiClient::School.new(
id:,
schoolCode: code,
studentEmailDomains: student_email_domains,
updatedAt: now,
createdAt: now,
discardedAt: nil
Expand Down
Loading