CSC/ECE 517 Spring 2025 - E2531 Refactor participants controller.rb

From Expertiza_Wiki
Jump to navigation Jump to search

Introduction

From the project description: "This project aims to refactor the participants_controller.rb and its helper class, participants_helper.rb, in the Expertiza system. The focus will be on improving code structure by implementing DRY and SOLID principles, enhancing readability with meaningful comments and clear naming conventions, and ensuring robustness through comprehensive RSpec testing." Since this is a refactoring project, we will also be utilizing Large Language Models (LLMs) to aid in development. More information on how we used them can be found in the section below.

Video Overview

https://youtu.be/4kfz0hrmSg4

Note: In the video it shows the test file at the end, but does not mention a last-minute change that numbers all of the tests to make it much easier to keep track of each of the tests and which ones are passing.

Use of Large Language Models (LLMs)

For this project, we are using ChatGPT as an LLM helper in determining what issues there are within the code. The use of LLMs in this project will likely be limited to probing for potential issues that exist within the system.

Below we have organized the following prompts and a summary of the output in a table:

Prompts LLM Output Summary
[Code from participants_controller.rb]

Are there any ways where we can refactor the code to improve readability, DRYness, and clarity of error handling?
It recognized some of the issues we, as a team, had already identified and suggested fixes that were similar to what we outlined: Redundant JSON Rendering Code[1] and Simplifying the Assignment of Participant Fields[2]. Additionally, it suggested minor changes that we could implement, such as using Participant.where(...) instead of Participant.all to filter out the code more efficiently instead of getting all database instances every time, as well as the use of strong parameters in add() and update_authorization() to avoid having to implement custom parsing techniques. It also recommends renaming the add() function to something more specific in relation to its purpose (adding a Participant to an Assignment.
Potential Redundant Tests in participants_controller_spec.rb The tests on line 222 and 231 are redundant as only the authorization differs. (Note: This should be investigated to see whether or not this is necessary)
Checking for issues with the current testing files The "Get Participant by ID call" test was returning a 201 status code instead of the more suitable 200 code

For more detailed information on what prompts we used and the output we got, please visit the page here: [3].

Requirements

Objectives:

  • Apply DRY principles to eliminate redundant code.
  • Ensure adherence to SOLID principles for better maintainability.
  • Use appropriate design patterns to optimize code architecture.
  • Write clear, informative comments and use meaningful naming conventions.
  • Test the refactored code extensively with RSpec to verify functionality.

Deliverables:

  • Refactored code for participants_controller.rb and helper class.
  • Comprehensive test suite and coverage report.
  • Updated and detailed documentation.
  • Video demonstration of the API functionality.

Existing Issues

  • There are some API functions that are currently not functioning properly, making it somewhat difficult to test
  • Some API functions are not returning the correct HTML status messages (e.g. test on line 136 returns 201 instead of 200)
  • Documentation and comments can be somewhat vague
    • In-line comments should be more descriptive, explaining what the code does in more details
    • Tests are not described via comments, but in name alone; some more descriptive comments that explain the tests in more detail would be ideal
  • Eliminate instances of redundant code
  • The method naming should be more descriptive

Design / Proposed Solution

Based on the previous implementation's controller diagram, we have created a new version that contains our proposed changes and modifications to participants_controller.rb.

Simplifying Redundant JSON Rendering Code

Many of the functions in the current implementation contain a conditional if-else statement that determines what HTML status code is returned alongside any necessary values that are requested. Due to its simple nature, it might be possible to reduce the amount of space this takes up by implementing a private helper method that would shorten the process.

Example:

  if participants.nil?
    render json: participants.errors, status: :unprocessable_entity
  else
    render json: participants, status: :ok
  end

Simplifying the Assignment of Participant Fields

The add() and update() both take up a significant amount of space to assign values to database fields and are essentially the same in functionality (in terms of this specific section. We can remove redundancy by creating a method that can remove this redundancy.

Example

  permissions = retrieve_participant_permissions(authorization)

  participant.authorization = authorization
  participant.can_submit = permissions[:can_submit]
  participant.can_review = permissions[:can_review]
  participant.can_take_quiz = permissions[:can_take_quiz]
  participant.can_mentor = permissions[:can_mentor]

Provide More Clarity in Function Names

There are some functions that are somewhat unclear/vague in participants_controller.rb. For example, the list_user_participants() and list_assignment_participants().

- Suggested Method Name Improvements

Old Method Name New Method Name Notes
list_user_participants render_participants_by_user_id Clearer and more descriptive
list_assignment_participants render_participants_by_assignment_id Clearer and more descriptive and is consistent with the other get method
add add_participant_to_assignment Describes both creation and authorization setup of participant to assignment
filter_user_participants filter_participants_by_user Highlights the purpose of the method
filter_assignment_participants filter_participants_by_assignment Highlights the purpose of the method

Improve Documentation and Comments

Some of the documentation and in-line comments can be somewhat vague. This can lead to misconceptions about how some functions work or how some of the code can function. Below are potential improvements we plan to make for function descriptions, though these may change with the final implementation.

Improved Method Comments

Method Original Comment Refactored Comment
render_participants_by_user_id Return a list of participants for a given user Retrieves all participants associated with the specified user ID
render_participants_by_assignment_id Return a list of participants for a given assignment Retrieves all participants linked to the specified assignment ID
show Return a specified participant Fetch and return the participant with the given ID
add_participant_to_assignment Assign the specified authorization to the participant and add them to an assignment Creates a new participant for a given assignment and assigns appropriate authorization and permissions
update_authorization Update the specified participant to the specified authorization Updates the participant’s authorization role and related permissions
destroy Delete a participant Deletes the participant identified by the given ID and returns a confirmation message
participant_params Permitted parameters for creating a Participant object Strong parameters: list of attributes allowed for participant creation or update
filter_participants_by_user Filters participants based on the provided user. Returns participants ordered by their IDs Returns a sorted list of participants filtered by user, if provided
filter_participants_by_assignment Filters participants based on the provided assignment. Returns participants ordered by their IDs Returns a sorted list of participants filtered by assignment, if provided
validate_authorization Validates that the authorization parameter is present and is one of the following valid authorizations: reader, reviewer, submitter, mentor Ensures authorization param is valid and returns it in lowercase; otherwise returns error response
find_user / find_assignment / find_participant Finds a user/assignment/participant by ID and returns it Fetches the corresponding record by ID; renders a not-found error if record does not exist

Proposed Testing Changes

  • Add more descriptive comments for tests
  • Add test that checks that authorizations are updated correctly
  • Add test that checks that inputted path parameters are not missing and valid inputs
  • Tests at Line 222 and Line 231 are redundant, as the only difference in the test is the authorization, which would not affect what happens if a participant is not found

Implementation

As this is a refactoring project, most of our work on the codebase involved cleaning up the current codebase to better align with foundational code style principles rather than introducing any of kind of new functionality. Our primary focus is to make the code more readable and easier to work with than before. We also took the liberty of fixing some minor errors that we found along the way.

Changelog

  • Naming changes to multiple functions for better clarity and consistency
  • Changed HTML status message for show() from 201 (Created) to 200 (OK) as it is a better fit
  • Created the assign_participant_permissions() method
  • Introduced two additional tests and removed a redundant one (see Testing Plan for more details)
  • Removed redundant queries to Participant.all() in both filtering methods
  • Updated method comments to be more descriptive (see comments table in Design)

Function Name Changes

The previous implementation had several functions that were strangely named or had names that could be easily misconstrued. The name changes remain the same from the proposal and design section, but for the purpose of clarity, the rationale for each of the functions will be provided below.

list_user_participants and list_assignment_participants

These two functions are meant to render a list of participants given a User or Assignment respectively. Based on the name alone, it is difficult to tell that the function would render a User/Assignment or Participants. In order to remedy this, we chose the names render_participants_by_user_id and render_participants_by_assignment_id. The word "list" was replaced by "render" in order to better denote that it is rendering something (json in this case) and the "by" is added to emphasize that the User and Assignment are being used as parameters to search for the Participants. This provides a much clearer picture of what these functions should do compared to before.

add

The add function's name was too vague to be particularly useful in identifying its purpose. To be more accurate, the function was meant to add the user to a given assignment, which would return a participant from the assignment's add_participant function. The new name of the function provides much more context to what it does (add_participant_to_assignment).

filter_user_participants and filter_assignment_participants

Similar to the list methods that were previously mentioned, it is difficult to tell what exactly these two functions do. While it can be easily seen that it filters something, what is being filtered can be easily mistaken (e.g. does it filter users by participants or participants by users?). The new names, filter_participants_by_user and filter_participants_by_assignment are much clearer in defining that the User and Assignment are being used as the filter parameters for the function.

HTML Status Code Change for GET Call

The status code being used for this was previously 201, denoting "successful creation". We have corrected this to a simple 200 code, which signifies an "ok" status message. This issue was actually identified by ChatGPT while we were dowsing for potential issues.

Test modified

# Tests whether a participant returns properly if provided with a valid Participant ID
response '200', 'Returns a participant' do
  let(:id) { participant2.id }

  run_test! do |response|
    data = JSON.parse(response.body)
    expect(data['user_id']).to eq(studenta.id)
    expect(data['assignment_id']).to eq(assignment2.id)
  end
end

Redundant Participant.all Queries Removed

This issue was initially caught by the LLM, ChatGPT, that the team was using in order to find potential issues. We found that simply using Participant.where() removed the need for the Participant.all query entirely while changing nothing else about the function, reducing the amount of queries necessary to call the filtering methods, filter_participants_by_user and filter_participants_by_assignment.

Example:

# New function
def filter_participants_by_user(user)
  participants = Participant.where(user_id: user.id) if user # This would simply get the participants given the proper user ID
  participants.order(:id)
end

# Old function
def filter_participants_by_user(user)
  participants = Participant.all() # This would obtain the entire list of participants rather than just the necessary ones
  participants = participants.where(user_id: user.id) if user
  participants.order(:id)
end

New Private Method: assign_participant_permissions()

The reasoning behind the addition of this method is because of a repeated piece of code that existed in the previous implementation:

permissions = retrieve_participant_permissions(authorization)

participant.authorization = authorization
participant.can_submit = permissions[:can_submit]
participant.can_review = permissions[:can_review]
participant.can_take_quiz = permissions[:can_take_quiz]
participant.can_mentor = permissions[:can_mentor]

This was repeated in two different contexts: when the Participant was updated or created. This is a violation of the DRY principle, as the same code is repeated multiple times where it could have been reduced to make it more easily readable. In order to resolve this issue, we simply turned this code snippet into a private method that can be called within participants_controller.rb:

  # An authorization string containing the participant's role is taken and used to determine
  # what permissions will be allocated to the participant. Each of these permissions will
  # then be assigned to the Participant's database permission attributes.
  #
  # @param [String] authorization: An authorization string that represents the participant's role
  # @param [Participant] participant: The participant whose authorization permissions are being updated
  def assign_participant_permissions(authorization, participant)
    # Call helper method from participants_helper to retrieve a dictionary containing the
    # appropriate permission boolean values for the role specified by the authorization string
    permissions = retrieve_participant_permissions(authorization)

    # Assigns each of the boolean permission values to their respective database counterparts
    participant.authorization = authorization
    participant.can_submit = permissions[:can_submit]
    participant.can_review = permissions[:can_review]
    participant.can_take_quiz = permissions[:can_take_quiz]
    participant.can_mentor = permissions[:can_mentor]
  end

Description:

An authorization string containing the participant's role is taken and used to determine what permissions will be allocated to the participant. Each of these permissions will then be assigned to the Participant's database permission attributes.

Parameters:

  • authorization: An authorization string that represents the participant's role. This can be either reader, reviewer, submitter, or mentor.
  • participant: The participant whose attributes are being updated. This is passed in to be modified by the function to be used to assign each of the participant's attribute values that are representative of its assigned role.

Behavior

  • Use retrieve_participant_permissions from participants_helper in order to get all necessary permission boolean values for a given participant role
  • Assign each of the permission boolean values (can_submit, can_review, can_take_quiz, can_mentor)
  • from the helper method to their respective database attribute counterparts in Participant

Post-Demo Changes

There were three notable topics that were brought up during the final demo presentation:

  • The renaming of get_participants_by_user and get_participants_by_assignment to something more suitable to its purpose in rendering the JSON of a list of participants
  • Consider the application of find_user and find_assignment
  • within participants_controller
  • Update the wiki to provide descriptions for each of the methods included in participants_controller (See Methods subsection in the Implementation section)

Renaming get_participants_by_user and get_participants_by_assignment

These two functions were renamed (once again) to something that better fit the functionality and purpose of the function. Since these functions are meant to render the lists of participants gotten from their respective parameters and use the ID values for each, both of the functions were renamed to render_participants_by_user_id and render_participants_by_assignment_id respectively. The renaming of the filter methods was also brought up, but those functions actually do use the objects themselves as parameters, so have been left as is for the time being.

find_user and find_assignment within participants_controller

We reconsidered the use of these methods within the controller since it was somewhat odd that it would have these methods. However, we concluded that, since the find methods are helper methods that are private and not open to be used by other parts of the code, to be fine to leave as is. This is mostly because they are simple helper methods that get the current user and assignment and return them, rendering an error if they do not exist.

Methods

render_participants_by_user_id

Description

Fetches all participants associated with the specified user. Primarily used to retrieve the list of participants which are equate to the user ID.

Parameters

  • user_id [Integer]: ID of the user

Returns

  • 200 OK: A JSON array of participant objects
  • 401 Unauthorized: If the user is not authorized for the action
  • 404 Not Found: If the user does not exist
  • 422 Unprocessable Entity: If the query fails unexpectedly

Notable Changes

  • Renamed from list_user_participants to render_participants_by_user_id

Code

  def render_participants_by_user_id
    user = find_user if params[:user_id].present?
    return if params[:user_id].present? && user.nil?

    participants = filter_participants_by_user(user)

    if participants.nil?
      render json: participants.errors, status: :unprocessable_entity
    else
      render json: participants, status: :ok
    end
  end

render_participants_by_assignment_id

Description

Fetches all participants associated with a specific assignment. Primarily used to retrieve the list of participants enrolled in an assignment for administrative purposes

Parameters

  • assignment_id [Integer]: ID of the assignment

Returns

  • 200 OK: A JSON array of participant objects
  • 401 Unauthorized: If the user is not authorized for the action
  • 404 Not Found: If the assignment does not exist
  • 422 Unprocessable Entity: If the query fails unexpectedly

Notable Changes

  • Renamed from list_assignment_participants to render_participants_by_assignment_id

Code

  def render_participants_by_assignment_id
    assignment = find_assignment if params[:assignment_id].present?
    return if params[:assignment_id].present? && assignment.nil?

    participants = filter_participants_by_assignment(assignment)

    if participants.nil?
      render json: participants.errors, status: :unprocessable_entity
    else
      render json: participants, status: :ok
    end
  end

show

Description

Fetches the details of a specific participant by their ID. Provides all attributes related to the participant, useful for detailed user tracking.

Parameters

  • id [Integer]: ID of the participant

Returns

  • 200 OK: JSON representation of the participant
  • 401 Unauthorized: If the user is not authorized
  • 404 Not Found: If the participant does not exist
  • 422 Unprocessable Entity: If the lookup fails

Notable Changes

  • Standardized participant lookup to return proper HTTP codes

Code

  def show
    participant = Participant.find(params[:id])

    if participant.nil?
      render json: participant.errors, status: :unprocessable_entity
    else
      render json: participant, status: :ok
    end
  end

add_participant_to_assignment

Description

Adds a user as a participant to an assignment with the specified authorization (role and permissions). This method ensures that appropriate permission settings are applied based on the participant's assigned role.

Parameters

  • participant[user_id] [Integer]: ID of the user
  • participant[assignment_id] [Integer]: ID of the assignment
  • authorization [String]: Role assigned to the participant (reader, reviewer, submitter, mentor)

Returns

  • 201 Created: Participant successfully added
  • 404 Not Found: If user or assignment is not found
  • 422 Unprocessable Entity: If authorization is invalid or save fails

Notable Changes

  • The naming convention was made similar to other methods

Code

  def add_participant_to_assignment
    user = find_user
    return unless user

    assignment = find_assignment
    return unless assignment

    authorization = validate_authorization
    return unless authorization

    participant = assignment.add_participant(user)
    assign_participant_permissions(authorization, participant)

    if participant.save
      render json: participant, status: :created
    else
      render json: participant.errors, status: :unprocessable_entity
    end
  end

update_authorization

Description

Updates the authorization (role) of an existing participant. Used when a participant's responsibilities or privileges within an assignment change and need to be updated.

Parameters

  • id [Integer]: ID of the participant
  • authorization [String]: New role to assign (reader, reviewer, submitter, mentor)

Returns

  • 201 Created: Authorization updated successfully
  • 401 Unauthorized: If the user is not authorized
  • 404 Not Found: If participant is not found
  • 422 Unprocessable Entity: If authorization is invalid

Notable Changes

  • Added validation for authorization role values

Code

  def update_authorization
    participant = find_participant
    return unless participant

    authorization = validate_authorization
    return unless authorization

    assign_participant_permissions(authorization, participant)

    if participant.save
      render json: participant, status: :created
    else
      render json: participant.errors, status: :unprocessable_entity
    end
  end

destroy

Description

Deletes a participant from the system. Optionally includes assignment and team context in the success message. This helps maintain clean records by removing users who drop out or are mistakenly added.

Parameters

  • id [Integer]: ID of the participant
  • assignment_id [Integer] (optional)
  • team_id [Integer] (optional)

Returns

  • 200 OK: Success message indicating deletion
  • 401 Unauthorized: If the user is not authorized
  • 404 Not Found: If participant is not found
  • 422 Unprocessable Entity: If deletion fails

Notable Changes

  • Enhanced success message based on team/assignment presence

Code

  def destroy
    participant = Participant.find_by(id: params[:id])
  
    if participant.nil?
      render json: { error: 'Not Found' }, status: :not_found
    elsif participant.destroy
      successful_deletion_message = if params[:team_id].nil?
                                      "Participant #{params[:id]} in Assignment #{params[:assignment_id]} has been deleted successfully!"
                                    else
                                      "Participant #{params[:id]} in Team #{params[:team_id]} of Assignment #{params[:assignment_id]} has been deleted successfully!"
                                    end
      render json: { message: successful_deletion_message }, status: :ok
    else
      render json: participant.errors, status: :unprocessable_entity
    end
  end

participants_params

Description

Defines the strong parameters allowed when creating or updating a participant. Ensures only permitted and validated fields are processed to protect against malicious data injection.

Parameters

  • participant: Hash containing fields like user_id, assignment_id, authorization, etc.

Returns

  • Permitted parameter hash for participant creation/update

Notable Changes

  • Updated to include all permission and assignment-related fields

Code

  def participant_params
    params.require(:participant).permit(:user_id, :assignment_id, :authorization, :can_submit,
                                        :can_review, :can_take_quiz, :can_mentor, :handle,
                                        :team_id, :join_team_request_id, :permission_granted,
                                        :topic, :current_stage, :stage_deadline)
  end

filter_participants_by_user

Description

Fetches participants associated with a given user, ordered by ID. Primarily used when displaying all participations of a single user across different assignments.

Parameters

  • user [User]: User object

Returns

  • Ordered list of participants for the user

Code

  def filter_participants_by_user(user)
    participants = Participant.where(user_id: user.id) if user
    participants.order(:id)
  end

filter_participants_by_assignment

Description

Fetches participants associated with a given assignment, ordered by ID. Helps instructors or admins manage the list of all users participating in a particular assignment.

Parameters

  • assignment [Assignment]: Assignment object

Returns

  • Ordered list of participants for the assignment


Code

  def filter_participants_by_assignment(assignment)
    participants = Participant.where(assignment_id: assignment.id) if assignment
    participants.order(:id)
  end

find_user

Description

Finds a user based on the provided user_id. Validates the existence of the user before performing operations like adding or retrieving participants

Parameters

  • user_id [Integer]: ID of the user

Returns

  • User object if found
  • 404 error if not found


Notable Changes

  • Centralized user lookup and error handling

Code

  def find_user
    user_id = params[:user_id]
    user = User.find_by(id: user_id)
    render json: { error: 'User not found' }, status: :not_found unless user
    user
  end

find_assignment

Description

Finds an assignment based on the provided assignment_id. Ensures that participant-related actions are linked to a valid and existing assignment record.

Parameters

  • assignment_id [Integer]: ID of the assignment

Returns

  • Assignment object if found
  • 404 error if not found

Notable Changes

  • Centralized assignment lookup and error handling

Code

  def find_assignment
    assignment_id = params[:assignment_id]
    assignment = Assignment.find_by(id: assignment_id)
    render json: { error: 'Assignment not found' }, status: :not_found unless assignment
    assignment
  end

find_participant

Description

Finds a participant based on the provided ID. Used internally to validate the participant’s presence before operations like update, show, or delete.

Parameters

  • id [Integer]: ID of the participant

Returns

  • Participant object if found
  • 404 error if not found

Notable Changes

  • Centralized participant lookup and error handling

Code

  def find_participant
    participant_id = params[:id]
    participant = Participant.find_by(id: participant_id)
    render json: { error: 'Participant not found' }, status: :not_found unless participant
    participant
  end

assign_participant_permissions

Description

Assigns permission flags to a participant based on the authorization role provided. It automatically maps permissions like can_submit, can_review, can_take_quiz, and can_mentor depending on the assigned role.

Parameters

  • authorization [String]: Role to assign
  • participant [Participant]: Participant object

Returns

  • Participant object with updated permissions

Notable Changes

  • Added dynamic permission setting based on role

Code

  def assign_participant_permissions(authorization, participant)
    # Call helper method from participants_helper to retrieve a dictionary containing the
    # appropriate permission boolean values for the role specified by the authorization string
    permissions = retrieve_participant_permissions(authorization)

    # Assigns each of the boolean permission values to their respective database counterparts
    participant.authorization = authorization
    participant.can_submit = permissions[:can_submit]
    participant.can_review = permissions[:can_review]
    participant.can_take_quiz = permissions[:can_take_quiz]
    participant.can_mentor = permissions[:can_mentor]
  end

validate_authorization

Description

Validates that the authorization string is present and one of the accepted roles. Ensures role-based access integrity by allowing only predefined, valid roles to be assigned to participants.

Parameters

  • authorization [String]: Authorization role

Returns

  • Validated authorization string
  • 422 error if invalid

Notable Changes

  • Standardized authorization validation across endpoints

Code

  def validate_authorization
    valid_authorizations = %w[reader reviewer submitter mentor]
    authorization = params[:authorization]
    authorization = authorization.downcase if authorization.present?

    unless authorization
      render json: { error: 'authorization is required' }, status: :unprocessable_entity
      return
    end

    unless valid_authorizations.include?(authorization)
      render json: { error: 'authorization not valid. Valid authorizations are: Reader, Reviewer, Submitter, Mentor' },
             status: :unprocessable_entity
      return
    end

    authorization
  end

Testing Plan

Of this implementation, also existing 25 tests are passing. We have also given each test their respective IDs within participants_controller_spec.rb as well so that they may be more easily identifiable. For more information about the previous testing plan, please refer to the previous implementation's page here: [4]

Below are any modifications to the existing test file (participants_controller_spec.rb) that we have made during the course of this project, alongside the descriptions and rationales behind them:

Tests

Below are the tests that are included in this implementation. We have updated the test descriptions to include more accurate and detailed information about each of the tests as well. Any tests that have been modified or new will be specified as such:

Test ID Test Description
1 Retrieve participants by valid user ID (should return participant data)
2 Retrieve participants by user ID with no participants (should return empty array)
3 Retrieve participants with non-existent user ID (should return 'User not found')
4 Retrieve participants by user ID with invalid token (should return 'Not Authorized')
5 Retrieve participants by valid assignment ID (should return participant data)
6 Retrieve participants by non-existent assignment ID (should return 'Assignment not found')
7 Retrieve participants by assignment ID with invalid token (should return 'Not Authorized')
8 Retrieve a participant by valid ID (should return participant details)
9 Retrieve a participant by non-existent ID (should return 'Not Found')
10 Retrieve a participant by ID with invalid token (should return 'Not Authorized')
11 Delete a participant by valid ID (should return deletion success message)
12 Delete a participant by non-existent ID (should return 'Not Found')
13 Delete a participant by ID with invalid token (should return 'Not Authorized')
14 Update participant authorization with valid data (should return updated authorization)
15 Update participant authorization with non-existent participant ID (should return 'Participant not found')
16 Update participant authorization with invalid role (should return 'authorization not valid')
17 Update participant authorization with invalid token (should return 'Not Authorized')
18 Add a participant successfully (should return participant data with authorization)
19 Add a participant that already exists (should return duplicate participant error)
20 Add a participant with non-existent user ID (should return 'User not found')
21 Add a participant with non-existent assignment ID (should return 'Assignment not found')
22 Add a participant with invalid authorization (should return 'authorization not valid')
23 Add a participant with unsupported authorization format (should return 'authorization not valid')
24 (NEW) Add a participant missing user_id in request body (should return 'User not found')
25 (NEW) Add a participant missing assignment_id in request body (should return 'Assignment not found')

Removed Tests

These are any tests that we have modified or removed from the previous testing plan. If the test ID is denoted by (Previously), that means that the ID shown was part of the previous implementation's testing plan.

Test ID Modification Description
(Previously) 22 This test was removed as it was redundant. While it is important to check the authorization of the participant that would be invalid, the invalid ID used for this test is a duplicate of the previous test, meaning the result is guaranteed to match that of the previous test regardless of what is meant to be tested.

Next Steps

Being able to implement this could potentially simplify rendering to the view somewhat in that it could handle simple HTML status code handling. Below are some examples of implementations to serve as a point of reference:

Example 1

# Checks if a target object (participant or participants) is not nil and then renders the object.
# If the object is nil, renders the object errors and sends a not_found_status and if the object
# exists, renders the object and sends a success status
# not_found_status default: :unprocessable_entity (422)
# success_status default: :ok (200)
def render_object_or_error(object, not_found_status: :unprocessable_entity, success_status: :ok)
  if object.nil?
    render json: { error: 'Resource not found' }, status: not_found_status
  elsif object.respond_to?(:errors) && object.errors.any?
    render json: object.errors, status: :unprocessable_entity
  else
    render json: object, status: success_status
end

This example is modeled to be specific to the most common use case for this function in the participants_controller.rb by returning the default 422 and 200 error and success codes that are used by three instances of JSON rendering where it checks for whether a list of participants or a single participant is nil. These default values for the status codes are mainly to cover those instances, but could be changed to something else if necessary. The object parameter is representative of the object that was meant to be rendered given the success or failure of the operation.

Example 2

def render_object_if(conditional, object, error_status, success_status)
  if conditional
    render json: { error: 'Resource not found' }, status: error_status
  else
    render json: object, status: success_status
end

This example is a more generic implementation of the function, mandating the error and success status codes rather than providing default values. The point of this was to emphasize readability when glancing over the use of this function. For example, being able to tell that seeing the function being used would immediately tell the reader what the success and failure states were immediately without having to find and inspect the function itself. The conditional parameter would also allow the function to act similarly to a more readable ternary statement made for rendering.

Implementing this successfully would reduce the amount of repetitive code being used within the controller, as the if-else view rendering is very common in participants_controller.rb. We were unable to implement it within our given timeframe, so we opted to divert our focus on other, important tasks that we needed to do.

Team

Mentor

  • Aniruddha Rajnekar <aarajnek@ncsu.edu>

Students

  • Akhil Adusumilli <aadusum@ncsu.edu>
  • Vansh Dodiya <vkdodiya@ncsu.edu>
  • Brian Huynh <bhhuynh@ncsu.edu>

Relevant Links

Pull Request [5]
GitHub Repository [6]
GitHub Project Board [7]
participants_controller.rb in Old Expertiza [8]
participants_helper.rb in Old Expertiza [9]