Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix filtering on new saved annotation page #5838

Merged
merged 6 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 2 additions & 2 deletions app/controllers/saved_annotations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ def new
authorize SavedAnnotation
@annotations = AnnotationPolicy::Scope.new(current_user, Annotation.all).resolve
@annotations = @annotations.where(saved_annotation_id: nil).where(user_id: current_user.id)
@courses = Course.where(id: @annotations.joins(:submission).pluck('submissions.course_id').uniq)
@exercises = Activity.where(id: @annotations.joins(:submission).pluck('submissions.exercise_id').uniq)
@courses = Course.where(id: @annotations.pluck('submission.course_id').uniq)
@exercises = Activity.where(id: @annotations.pluck('submission.exercise_id').uniq)
@annotations = apply_scopes(@annotations.order_by_created_at(:DESC))
.includes(:course).includes(:user).includes(:submission)
.paginate(page: parse_pagination_param(params[:page]), per_page: parse_pagination_param(params[:per_page]))
Expand Down
8 changes: 4 additions & 4 deletions app/policies/annotation_policy.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
class AnnotationPolicy < ApplicationPolicy
class Scope < ApplicationPolicy::Scope
def resolve
annotations = scope.joins('INNER JOIN submissions AS submission ON submission.id = annotations.submission_id')
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor join to use ActiveRecord methods instead of raw SQL

Using raw SQL in joins reduces readability and may introduce SQL injection risks. Consider utilizing ActiveRecord's query interface to define the join with a table alias.

You can refactor the join as follows:

-annotations = scope.joins('INNER JOIN submissions AS submission ON submission.id = annotations.submission_id')
+annotations = scope.joins(:submission)

Since Annotation belongs to Submission, you can use the association to perform the join:

annotations = scope.joins(:submission)

If you need to reference the table with an alias due to pluralization issues, you can use joins with a string that's safer by leveraging sanitize_sql:

annotations = scope.joins(ActiveRecord::Base.sanitize_sql(['INNER JOIN submissions submission ON submission.id = annotations.submission_id']))

However, it's often better to adjust the query to avoid raw SQL altogether.

if user&.zeus?
scope.all
annotations.all
elsif user
common = scope.joins(:submission)
common.released.where(submissions: { user: user }).or(common.where(submissions: { course_id: user.administrating_courses.map(&:id) }))
annotations.released.where(submission: { user: user }).or(annotations.where(submission: { course_id: user.administrating_courses.map(&:id) }))
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Simplify complex where conditions for better readability

The where clause combines multiple conditions in a way that can be difficult to read and maintain. Breaking it into smaller scopes or using predicate methods can enhance clarity.

Consider refactoring as follows:

-annotations.released.where(submission: { user: user }).or(annotations.where(submission: { course_id: user.administrating_courses.map(&:id) }))
+user_annotations = annotations.released.where(submission: { user: user })
+admin_annotations = annotations.where(submission: { course_id: user.administrating_courses.ids })
+user_annotations.or(admin_annotations)

This separates the conditions and makes the logic more transparent.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
annotations.released.where(submission: { user: user }).or(annotations.where(submission: { course_id: user.administrating_courses.map(&:id) }))
user_annotations = annotations.released.where(submission: { user: user })
admin_annotations = annotations.where(submission: { course_id: user.administrating_courses.ids })
user_annotations.or(admin_annotations)

else
scope.none
annotations.none
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure test coverage for the updated resolve method

With changes to the resolve method, it's important to update the test suite to cover the new logic paths, especially since we've introduced a new variable and modified the query structure.

This aligns with previous learnings about updating tests when introducing new variables. Would you like assistance in writing additional tests to ensure comprehensive coverage?

end
end
end
Expand Down
21 changes: 21 additions & 0 deletions test/controllers/saved_annotation_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,25 @@ def setup

assert_response :forbidden
end

test 'staff should be able to filter existing annotations on new page' do
get new_saved_annotation_path

assert_response :success

get new_saved_annotation_path, params: { exercise_id: 1 }

assert_response :success
end
Comment on lines +53 to +61
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance test coverage for staff filtering

The test correctly checks if staff can access the new saved annotation page with and without filtering. However, it could be improved to verify the actual filtering functionality.

Consider the following improvements:

  1. Add setup code to create test data (e.g., saved annotations with different exercise_ids).
  2. Verify the content of the response, not just the status code.
  3. Assert that the filtered results only contain annotations for the specified exercise_id.

Here's a suggested improvement:

test 'staff should be able to filter existing annotations on new page' do
  exercise1 = create(:exercise)
  exercise2 = create(:exercise)
  annotation1 = create(:saved_annotation, user: @user, course: @course, exercise: exercise1)
  annotation2 = create(:saved_annotation, user: @user, course: @course, exercise: exercise2)

  get new_saved_annotation_path
  assert_response :success
  assert_select 'your-selector-for-annotation', 2

  get new_saved_annotation_path, params: { exercise_id: exercise1.id }
  assert_response :success
  assert_select 'your-selector-for-annotation', 1
  assert_select 'your-selector-for-annotation-title', annotation1.title
end

Replace 'your-selector-for-annotation' with the actual CSS selector used in your view to represent annotations.


test 'zeus should be able to filter existing annotations on new page' do
sign_in users(:zeus)
get new_saved_annotation_path

assert_response :success

get new_saved_annotation_path, params: { exercise_id: 1 }

assert_response :success
end
Comment on lines +63 to +72
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor zeus test and enhance coverage

The zeus test is very similar to the staff test, which suggests an opportunity for refactoring to reduce code duplication. Additionally, it has the same limitations in terms of verifying the actual filtering functionality.

Consider the following improvements:

  1. Refactor the common testing logic into a shared method.
  2. Enhance the test to verify the content of the response, similar to the suggestions for the staff test.
  3. Ensure that zeus users can only see their own annotations in the filtered results.

Here's a suggested refactoring and improvement:

def assert_can_filter_annotations(user)
  sign_in user
  exercise1 = create(:exercise)
  exercise2 = create(:exercise)
  annotation1 = create(:saved_annotation, user: user, course: @course, exercise: exercise1)
  annotation2 = create(:saved_annotation, user: user, course: @course, exercise: exercise2)

  get new_saved_annotation_path
  assert_response :success
  assert_select 'your-selector-for-annotation', 2

  get new_saved_annotation_path, params: { exercise_id: exercise1.id }
  assert_response :success
  assert_select 'your-selector-for-annotation', 1
  assert_select 'your-selector-for-annotation-title', annotation1.title
end

test 'staff should be able to filter existing annotations on new page' do
  assert_can_filter_annotations(@user)
end

test 'zeus should be able to filter existing annotations on new page' do
  assert_can_filter_annotations(users(:zeus))
end

This refactoring reduces duplication and ensures consistent testing across user types. Adjust the selectors and assertions as needed to match your actual view structure.

end