|
1 | 1 | # frozen_string_literal: true
|
2 | 2 |
|
3 | 3 | class ProfileApiClient
|
| 4 | + SAFEGUARDING_FLAGS = { |
| 5 | + teacher: 'school:teacher', |
| 6 | + owner: 'school:owner' |
| 7 | + }.freeze |
| 8 | + |
| 9 | + class Error < StandardError; end |
| 10 | + |
| 11 | + class CreateStudent422Error < Error |
| 12 | + DEFAULT_ERROR = 'unknown error' |
| 13 | + ERRORS = { |
| 14 | + 'ERR_USER_EXISTS' => 'username has already been taken', |
| 15 | + 'ERR_INVALID' => 'unknown validation error', |
| 16 | + 'ERR_INVALID_PASSWORD' => 'password is invalid', |
| 17 | + 'ERR_UNKNOWN' => DEFAULT_ERROR |
| 18 | + }.freeze |
| 19 | + |
| 20 | + attr_reader :username, :error |
| 21 | + |
| 22 | + def initialize(error) |
| 23 | + @username = error['username'] |
| 24 | + @error = ERRORS.fetch(error['error'], DEFAULT_ERROR) |
| 25 | + |
| 26 | + super "Student not created in Profile API (status code 422, username '#{@username}', error '#{@error}')" |
| 27 | + end |
| 28 | + end |
| 29 | + |
4 | 30 | class << self
|
5 | 31 | # TODO: Replace with HTTP requests once the profile API has been built.
|
6 | 32 |
|
7 |
| - def create_school(token:, school:) |
8 |
| - return { 'id' => school.id, 'schoolCode' => school.code } if ENV['BYPASS_OAUTH'].present? |
| 33 | + def create_school(token:, id:, code:) |
| 34 | + return { 'id' => id, 'schoolCode' => code } if ENV['BYPASS_OAUTH'].present? |
9 | 35 |
|
10 |
| - response = connection.post('/api/v1/schools') do |request| |
11 |
| - apply_default_headers(request, token) |
| 36 | + response = connection(token).post('/api/v1/schools') do |request| |
12 | 37 | request.body = {
|
13 |
| - id: school.id, |
14 |
| - schoolCode: school.code |
15 |
| - }.to_json |
| 38 | + id:, |
| 39 | + schoolCode: code |
| 40 | + } |
16 | 41 | end
|
17 | 42 |
|
18 |
| - raise "School not created in Profile API. HTTP response code: #{response.status}" unless response.status == 201 |
| 43 | + raise "School not created in Profile API (status code #{response.status})" unless response.status == 201 |
19 | 44 |
|
20 |
| - JSON.parse(response.body) |
| 45 | + response.body |
21 | 46 | end
|
22 | 47 |
|
23 | 48 | # The API should enforce these constraints:
|
@@ -142,19 +167,24 @@ def list_school_students(token:, organisation_id:)
|
142 | 167 | # The API should respond:
|
143 | 168 | # - 404 Not Found if the user doesn't exist
|
144 | 169 | # - 422 Unprocessable if the constraints are not met
|
145 |
| - def create_school_student(token:, username:, password:, name:, organisation_id:) |
| 170 | + # rubocop:disable Metrics/AbcSize |
| 171 | + def create_school_student(token:, username:, password:, name:, school_id:) |
146 | 172 | return nil if token.blank?
|
147 | 173 |
|
148 |
| - _ = username |
149 |
| - _ = password |
150 |
| - _ = name |
151 |
| - _ = organisation_id |
| 174 | + response = connection(token).post("/api/v1/schools/#{school_id}/students") do |request| |
| 175 | + request.body = [{ |
| 176 | + name: name.strip, |
| 177 | + username: username.strip, |
| 178 | + password: password.strip |
| 179 | + }] |
| 180 | + end |
152 | 181 |
|
153 |
| - # TODO: We should make Faraday raise a Ruby error for a non-2xx status |
154 |
| - # code so that SchoolStudent::Create propagates the error in the response. |
155 |
| - response = {} |
156 |
| - response.deep_symbolize_keys |
| 182 | + raise CreateStudent422Error, response.body['errors'].first if response.status == 422 |
| 183 | + raise "Student not created in Profile API (status code #{response.status})" unless response.status == 201 |
| 184 | + |
| 185 | + response.body.deep_symbolize_keys |
157 | 186 | end
|
| 187 | + # rubocop:enable Metrics/AbcSize |
158 | 188 |
|
159 | 189 | # The API should enforce these constraints:
|
160 | 190 | # - The token has the school-owner or school-teacher role for the given organisation ID
|
@@ -198,18 +228,46 @@ def delete_school_student(token:, student_id:, organisation_id:)
|
198 | 228 | response.deep_symbolize_keys
|
199 | 229 | end
|
200 | 230 |
|
201 |
| - private |
| 231 | + def safeguarding_flags(token:) |
| 232 | + response = connection(token).get('/api/v1/safeguarding-flags') |
202 | 233 |
|
203 |
| - def connection |
204 |
| - Faraday.new(ENV.fetch('IDENTITY_URL')) |
| 234 | + unless response.status == 200 |
| 235 | + raise "Safeguarding flags cannot be retrieved from Profile API (status code #{response.status})" |
| 236 | + end |
| 237 | + |
| 238 | + response.body.map(&:deep_symbolize_keys) |
| 239 | + end |
| 240 | + |
| 241 | + def create_safeguarding_flag(token:, flag:) |
| 242 | + response = connection(token).post('/api/v1/safeguarding-flags') do |request| |
| 243 | + request.body = { flag: } |
| 244 | + end |
| 245 | + |
| 246 | + return if response.status == 201 || response.status == 303 |
| 247 | + |
| 248 | + raise "Safeguarding flag not created in Profile API (status code #{response.status})" |
205 | 249 | end
|
206 | 250 |
|
207 |
| - def apply_default_headers(request, token) |
208 |
| - request.headers['Accept'] = 'application/json' |
209 |
| - request.headers['Authorization'] = "Bearer #{token}" |
210 |
| - request.headers['Content-Type'] = 'application/json' |
211 |
| - request.headers['X-API-KEY'] = ENV.fetch('PROFILE_API_KEY') |
212 |
| - request |
| 251 | + def delete_safeguarding_flag(token:, flag:) |
| 252 | + response = connection(token).delete("/api/v1/safeguarding-flags/#{flag}") |
| 253 | + |
| 254 | + return if response.status == 204 |
| 255 | + |
| 256 | + raise "Safeguarding flag not deleted from Profile API (status code #{response.status})" |
| 257 | + end |
| 258 | + |
| 259 | + private |
| 260 | + |
| 261 | + def connection(token) |
| 262 | + Faraday.new(ENV.fetch('IDENTITY_URL')) do |faraday| |
| 263 | + faraday.request :json |
| 264 | + faraday.response :json |
| 265 | + faraday.headers = { |
| 266 | + 'Accept' => 'application/json', |
| 267 | + 'Authorization' => "Bearer #{token}", |
| 268 | + 'X-API-KEY' => ENV.fetch('PROFILE_API_KEY') |
| 269 | + } |
| 270 | + end |
213 | 271 | end
|
214 | 272 | end
|
215 | 273 | end
|
0 commit comments