Skip to content

Commit 874e404

Browse files
authored
Enable classes to be loaded from school and class codes (#525)
## Status closes #517
1 parent ceb2643 commit 874e404

File tree

2 files changed

+189
-67
lines changed

2 files changed

+189
-67
lines changed

app/controllers/api/school_classes_controller.rb

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
module Api
44
class SchoolClassesController < ApiController
55
before_action :authorize_user
6-
load_and_authorize_resource :school
7-
load_and_authorize_resource :school_class, through: :school, through_association: :classes
6+
before_action :load_and_authorize_school
7+
before_action :load_and_authorize_school_class
88

99
def index
1010
school_classes = @school.classes.accessible_by(current_ability)
@@ -53,6 +53,29 @@ def destroy
5353

5454
private
5555

56+
def load_and_authorize_school
57+
@school = if params[:school_id].match?(/\d\d-\d\d-\d\d/)
58+
School.find_by(code: params[:school_id])
59+
else
60+
School.find(params[:school_id])
61+
end
62+
authorize! :read, @school
63+
end
64+
65+
def load_and_authorize_school_class
66+
if %w[index create].include?(params[:action])
67+
authorize! params[:action].to_sym, SchoolClass
68+
else
69+
@school_class = if params[:id].match?(/\d\d-\d\d-\d\d/)
70+
@school.classes.find_by(code: params[:id])
71+
else
72+
@school.classes.find(params[:id])
73+
end
74+
75+
authorize! params[:action].to_sym, @school_class
76+
end
77+
end
78+
5679
def school_class_params
5780
# A school teacher may only create classes they own.
5881
params.require(:school_class).permit(:name, :description)

spec/features/school_class/showing_a_school_class_spec.rb

Lines changed: 164 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -14,93 +14,192 @@
1414
let(:teacher) { create(:teacher, school:, name: 'School Teacher') }
1515
let(:owner) { create(:owner, school:) }
1616

17-
it 'responds 200 OK' do
18-
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
19-
expect(response).to have_http_status(:ok)
20-
end
17+
context 'when school and class ids are provided' do
18+
it 'responds 200 OK' do
19+
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
20+
expect(response).to have_http_status(:ok)
21+
end
2122

22-
it 'responds 200 OK when the user is the class teacher' do
23-
authenticated_in_hydra_as(teacher)
23+
it 'responds 200 OK when the user is the class teacher' do
24+
authenticated_in_hydra_as(teacher)
2425

25-
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
26-
expect(response).to have_http_status(:ok)
27-
end
26+
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
27+
expect(response).to have_http_status(:ok)
28+
end
2829

29-
it 'responds 200 OK when the user is a student in the class' do
30-
student = create(:student, school:)
31-
authenticated_in_hydra_as(student)
32-
create(:class_student, school_class:, student_id: student.id)
30+
it 'responds 200 OK when the user is a student in the class' do
31+
student = create(:student, school:)
32+
authenticated_in_hydra_as(student)
33+
create(:class_student, school_class:, student_id: student.id)
3334

34-
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
35-
expect(response).to have_http_status(:ok)
36-
end
35+
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
36+
expect(response).to have_http_status(:ok)
37+
end
3738

38-
it 'responds with the school class JSON' do
39-
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
40-
data = JSON.parse(response.body, symbolize_names: true)
39+
it 'responds with the school class JSON' do
40+
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
41+
data = JSON.parse(response.body, symbolize_names: true)
4142

42-
expect(data[:name]).to eq('Test School Class')
43-
end
43+
expect(data[:name]).to eq('Test School Class')
44+
end
4445

45-
it 'includes the school class code in the response' do
46-
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
47-
data = JSON.parse(response.body, symbolize_names: true)
46+
it 'includes the school class code in the response' do
47+
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
48+
data = JSON.parse(response.body, symbolize_names: true)
4849

49-
expect(data[:code]).to eq(school_class.code)
50-
end
50+
expect(data[:code]).to eq(school_class.code)
51+
end
5152

52-
it 'responds with the teacher JSON' do
53-
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
54-
data = JSON.parse(response.body, symbolize_names: true)
53+
it 'responds with the teacher JSON' do
54+
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
55+
data = JSON.parse(response.body, symbolize_names: true)
5556

56-
expect(data[:teachers].first[:name]).to eq('School Teacher')
57-
end
57+
expect(data[:teachers].first[:name]).to eq('School Teacher')
58+
end
5859

59-
it "responds with nil attributes for the teacher if their user profile doesn't exist" do
60-
stub_user_info_api_for_unknown_users(user_id: teacher.id)
60+
it "responds with nil attributes for the teacher if their user profile doesn't exist" do
61+
stub_user_info_api_for_unknown_users(user_id: teacher.id)
6162

62-
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
63-
data = JSON.parse(response.body, symbolize_names: true)
63+
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
64+
data = JSON.parse(response.body, symbolize_names: true)
6465

65-
expect(data[:teacher_name]).to be_nil
66-
end
66+
expect(data[:teacher_name]).to be_nil
67+
end
6768

68-
it 'responds 404 Not Found when no school exists' do
69-
get("/api/schools/not-a-real-id/classes/#{school_class.id}", headers:)
70-
expect(response).to have_http_status(:not_found)
71-
end
69+
it 'responds 404 Not Found when no school exists' do
70+
get("/api/schools/not-a-real-id/classes/#{school_class.id}", headers:)
71+
expect(response).to have_http_status(:not_found)
72+
end
7273

73-
it 'responds 404 Not Found when no school class exists' do
74-
get("/api/schools/#{school.id}/classes/not-a-real-id", headers:)
75-
expect(response).to have_http_status(:not_found)
76-
end
74+
it 'responds 404 Not Found when no school class exists' do
75+
get("/api/schools/#{school.id}/classes/not-a-real-id", headers:)
76+
expect(response).to have_http_status(:not_found)
77+
end
7778

78-
it 'responds 401 Unauthorized when no token is given' do
79-
get "/api/schools/#{school.id}/classes/#{school_class.id}"
80-
expect(response).to have_http_status(:unauthorized)
81-
end
79+
it 'responds 401 Unauthorized when no token is given' do
80+
get "/api/schools/#{school.id}/classes/#{school_class.id}"
81+
expect(response).to have_http_status(:unauthorized)
82+
end
8283

83-
it 'responds 403 Forbidden when the user is a school-owner for a different school' do
84-
school = create(:school, id: SecureRandom.uuid)
85-
school_class.update!(school_id: school.id)
84+
it 'responds 403 Forbidden when the user is a school-owner for a different school' do
85+
school = create(:school, id: SecureRandom.uuid)
86+
school_class.update!(school_id: school.id)
8687

87-
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
88-
expect(response).to have_http_status(:forbidden)
89-
end
88+
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
89+
expect(response).to have_http_status(:forbidden)
90+
end
91+
92+
it 'responds 403 Forbidden when the user is not the school-teacher for the class' do
93+
teacher = create(:teacher, school:)
94+
authenticated_in_hydra_as(teacher)
9095

91-
it 'responds 403 Forbidden when the user is not the school-teacher for the class' do
92-
teacher = create(:teacher, school:)
93-
authenticated_in_hydra_as(teacher)
96+
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
97+
expect(response).to have_http_status(:forbidden)
98+
end
9499

95-
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
96-
expect(response).to have_http_status(:forbidden)
100+
it 'responds 403 Forbidden when the user is not a school-student for the class' do
101+
student = create(:student, school:)
102+
authenticated_in_hydra_as(student)
103+
104+
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
105+
expect(response).to have_http_status(:forbidden)
106+
end
97107
end
98108

99-
it 'responds 403 Forbidden when the user is not a school-student for the class' do
100-
student = create(:student, school:)
101-
authenticated_in_hydra_as(student)
109+
context 'when school and class codes are provided' do
110+
before do
111+
school.verify!
112+
end
113+
114+
it 'responds 200 OK' do
115+
get("/api/schools/#{school.code}/classes/#{school_class.code}", headers:)
116+
expect(response).to have_http_status(:ok)
117+
end
118+
119+
it 'responds 200 OK when the user is the class teacher' do
120+
authenticated_in_hydra_as(teacher)
121+
122+
get("/api/schools/#{school.code}/classes/#{school_class.code}", headers:)
123+
expect(response).to have_http_status(:ok)
124+
end
125+
126+
it 'responds 200 OK when the user is a student in the class' do
127+
student = create(:student, school:)
128+
authenticated_in_hydra_as(student)
129+
create(:class_student, school_class:, student_id: student.id)
130+
131+
get("/api/schools/#{school.code}/classes/#{school_class.code}", headers:)
132+
expect(response).to have_http_status(:ok)
133+
end
134+
135+
it 'responds with the school class JSON' do
136+
get("/api/schools/#{school.code}/classes/#{school_class.code}", headers:)
137+
data = JSON.parse(response.body, symbolize_names: true)
138+
139+
expect(data[:name]).to eq('Test School Class')
140+
end
141+
142+
it 'includes the school class code in the response' do
143+
get("/api/schools/#{school.code}/classes/#{school_class.code}", headers:)
144+
data = JSON.parse(response.body, symbolize_names: true)
145+
146+
expect(data[:code]).to eq(school_class.code)
147+
end
148+
149+
it 'responds with the teacher JSON' do
150+
get("/api/schools/#{school.code}/classes/#{school_class.code}", headers:)
151+
data = JSON.parse(response.body, symbolize_names: true)
152+
153+
expect(data[:teachers].first[:name]).to eq('School Teacher')
154+
end
155+
156+
it "responds with nil attributes for the teacher if their user profile doesn't exist" do
157+
stub_user_info_api_for_unknown_users(user_id: teacher.id)
158+
159+
get("/api/schools/#{school.code}/classes/#{school_class.code}", headers:)
160+
data = JSON.parse(response.body, symbolize_names: true)
161+
162+
expect(data[:teacher_name]).to be_nil
163+
end
164+
165+
it 'responds 404 Not Found when no school exists' do
166+
get("/api/schools/not-a-real-code/classes/#{school_class.code}", headers:)
167+
expect(response).to have_http_status(:not_found)
168+
end
169+
170+
it 'responds 404 Not Found when no school class exists' do
171+
get("/api/schools/#{school.code}/classes/not-a-real-code", headers:)
172+
expect(response).to have_http_status(:not_found)
173+
end
174+
175+
it 'responds 401 Unauthorized when no token is given' do
176+
get "/api/schools/#{school.code}/classes/#{school_class.code}"
177+
expect(response).to have_http_status(:unauthorized)
178+
end
179+
180+
it 'responds 403 Forbidden when the user is a school-owner for a different school' do
181+
school = create(:school, id: SecureRandom.uuid)
182+
school.verify!
183+
school_class.update!(school_id: school.id)
184+
185+
get("/api/schools/#{school.code}/classes/#{school_class.code}", headers:)
186+
expect(response).to have_http_status(:forbidden)
187+
end
188+
189+
it 'responds 403 Forbidden when the user is not the school-teacher for the class' do
190+
teacher = create(:teacher, school:)
191+
authenticated_in_hydra_as(teacher)
192+
193+
get("/api/schools/#{school.code}/classes/#{school_class.code}", headers:)
194+
expect(response).to have_http_status(:forbidden)
195+
end
196+
197+
it 'responds 403 Forbidden when the user is not a school-student for the class' do
198+
student = create(:student, school:)
199+
authenticated_in_hydra_as(student)
102200

103-
get("/api/schools/#{school.id}/classes/#{school_class.id}", headers:)
104-
expect(response).to have_http_status(:forbidden)
201+
get("/api/schools/#{school.code}/classes/#{school_class.code}", headers:)
202+
expect(response).to have_http_status(:forbidden)
203+
end
105204
end
106205
end

0 commit comments

Comments
 (0)