-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This integrates with the Provider API in an API-first way – e.g. by calling out to the API rather than relying on an overnight "ETL" sync job to cache Provider records in a local Active Record model. However, I'm putting this work on hold for now because it's become increasingly clear that an overnight ETL would grant us more flexibility in how we interact with Provider data. It currently seems to be the more pragmatic approach, so I'm putting this API-first implementation on ice for now.
- Loading branch information
1 parent
3835ce3
commit c0a863d
Showing
4 changed files
with
126 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
require "net/http" | ||
|
||
module TeacherTrainingCourses | ||
class ApiClient | ||
BASE_URL = | ||
"https://api.publish-teacher-training-courses.service.gov.uk/api/public/v1".freeze | ||
|
||
class << self | ||
def get(endpoint) | ||
request(method: :get, endpoint:) | ||
end | ||
|
||
def get_all_pages(endpoint) | ||
results = [] | ||
is_last_page = false | ||
|
||
until is_last_page | ||
response = request(method: :get, endpoint:) | ||
results.concat response.fetch("data") | ||
|
||
if (next_page = response.dig("links", "next")) | ||
endpoint = next_page.delete_prefix(BASE_URL) | ||
else | ||
is_last_page = true | ||
end | ||
end | ||
|
||
results | ||
end | ||
|
||
private | ||
|
||
def request(method:, endpoint:) | ||
url = URI("#{BASE_URL}#{endpoint}") | ||
response = Net::HTTP.public_send(method, url) | ||
JSON.parse(response) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
module TeacherTrainingCourses | ||
class Provider | ||
ATTRIBUTES = %i[code name email website ukprn accredited_body].freeze | ||
|
||
attr_reader(*ATTRIBUTES) | ||
|
||
def initialize(_code, attributes) | ||
ATTRIBUTES.each do |attribute| | ||
attribute = attribute.to_s | ||
instance_variable_set("@#{attribute}", attributes[attribute]) | ||
end | ||
end | ||
|
||
class << self | ||
def all | ||
providers_from_api.map { |code, attributes| new(code, attributes) } | ||
end | ||
|
||
def find(code) | ||
if (attributes = providers_from_api[code]) | ||
new(code, attributes) | ||
else | ||
raise ProviderNotFound, code | ||
end | ||
end | ||
|
||
private | ||
|
||
def providers_from_api | ||
@providers_from_api ||= Rails.cache.fetch("providers_from_api", expires_in: 1.hour) do | ||
# Fetch all providers from the Teacher Training Courses API | ||
providers = ApiClient.get_all_pages("/recruitment_cycles/2024/providers") | ||
|
||
# Drop attributes we don't need | ||
providers = providers.map do |provider| | ||
provider["attributes"].select { |key, _value| ATTRIBUTES.include?(key.to_sym) } | ||
end | ||
|
||
# Transform to a Hash indexed by provider code | ||
providers.index_by { |provider| provider["code"] } | ||
end | ||
end | ||
end | ||
|
||
class ProviderNotFound < StandardError | ||
def initialize(code) | ||
super("Couldn't find provider with code '#{code}'") | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
require "rails_helper" | ||
|
||
RSpec.describe TeacherTrainingCourses::ApiClient do | ||
describe ".get" do | ||
it "makes a GET request to the API" do | ||
stub_providers | ||
debugger | ||
expect(described_class.get("/recruitment_cycles/2024/providers")).to eq("Hello") | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
require "rails_helper" | ||
|
||
RSpec.describe TeacherTrainingCourses::Provider do | ||
describe ".all" do | ||
it "makes a GET request to the API" do | ||
stub_providers | ||
expect(described_class.all).to eq [] | ||
end | ||
end | ||
|
||
describe ".find" do | ||
it "gets a specific provider by its code" do | ||
stub_providers([ | ||
build(:teacher_training_courses_provider, code: "T92", name: "Example provider") | ||
]) | ||
|
||
provider = described_class.find("T92") | ||
|
||
expect(provider).to be_a TeacherTrainingCourses::Provider | ||
expect(provider.code).to eq "T92" | ||
expect(provider.name).to eq "Example provider" | ||
end | ||
end | ||
end |