Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
fbbe467
Pending changes exported from your codespace
vihar2712 Jun 24, 2025
755012e
grades controller finished
vihar2712 Aug 5, 2025
a1878ad
Merged main branch into join-team-requests-and-invitations
vihar2712 Aug 11, 2025
cd798c6
team hierarchy still unresolved. authorization module included.
vihar2712 Aug 21, 2025
65bf36b
/api/v1 removed
vihar2712 Oct 1, 2025
61e8698
modified invitation model
vihar2712 Oct 1, 2025
8985131
modified student_teams controller - made use of serializer, sending c…
vihar2712 Oct 6, 2025
04e31e6
methods inside student_teams added
vihar2712 Oct 12, 2025
9f8d758
retract, accept, reject invitation working
vihar2712 Oct 13, 2025
f0ebf63
added authorization inside invitations controller
vihar2712 Oct 17, 2025
4c80523
modified invitations controller
vihar2712 Oct 27, 2025
61a437b
added invitations sent by participant route and method
vihar2712 Oct 29, 2025
ec90caa
added methods for performing crud operations on advertisement inside …
vihar2712 Nov 5, 2025
4d25216
Advertisements and JoinTeamRequests (#248)
vihar2712 Dec 25, 2025
bf50188
Pending changes exported from your codespace
vihar2712 Jun 24, 2025
981c8af
grades controller finished
vihar2712 Aug 5, 2025
de27c99
team hierarchy still unresolved. authorization module included.
vihar2712 Aug 21, 2025
82583d1
/api/v1 removed
vihar2712 Oct 1, 2025
5f68a47
modified invitation model
vihar2712 Oct 1, 2025
384d4b6
modified student_teams controller - made use of serializer, sending c…
vihar2712 Oct 6, 2025
68724ad
methods inside student_teams added
vihar2712 Oct 12, 2025
fc5388c
retract, accept, reject invitation working
vihar2712 Oct 13, 2025
e765e2e
added authorization inside invitations controller
vihar2712 Oct 17, 2025
5316440
modified invitations controller
vihar2712 Oct 27, 2025
eca90c3
added invitations sent by participant route and method
vihar2712 Oct 29, 2025
e2750cf
added methods for performing crud operations on advertisement inside …
vihar2712 Nov 5, 2025
eb826b5
Merge branch 'advertisements' of https://github.com/expertiza/reimple…
vihar2712 Dec 26, 2025
0eee19c
synced to all the updated changes of main
vihar2712 Dec 26, 2025
f66f1f4
refactored syntax issues and resolved test files
vihar2712 Dec 26, 2025
603587a
tests modified
vihar2712 Jan 19, 2026
94ff3a6
attributes added in participant and assignment serializer
vihar2712 Jan 19, 2026
edf692e
ignore seed files for lint check
vihar2712 Jan 19, 2026
230d9a4
Merge latest changes from main
vihar2712 Jan 28, 2026
154cdbb
deleted unncessary dump files
vihar2712 Jan 28, 2026
a0ec1d3
made changes caused due to merging main andn fixed CR errors
vihar2712 Feb 2, 2026
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
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ db/schema.rb linguist-generated

# Mark any vendored files as having been vendored.
vendor/* linguist-vendored

* text=auto eol=lf
240 changes: 205 additions & 35 deletions app/controllers/join_team_requests_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,84 +8,220 @@ class JoinTeamRequestsController < ApplicationController
before_action :check_team_status, only: [:create]

# This filter runs before the specified actions, finding the join team request
before_action :find_request, only: %i[show update destroy decline]
before_action :find_request, only: %i[show update destroy decline accept]

#checks if the current user is a student
# This filter ensures the request is still pending before processing
before_action :ensure_request_pending, only: %i[accept decline]

# Centralized authorization method
def action_allowed?
@current_user.student?
case params[:action]
when 'index'
# Only administrators can view all join team requests
current_user_has_admin_privileges?

when 'create'
# Any student can create a join team request
current_user_has_student_privileges?

when 'show'
# The participant who made the request OR any team member can view it
return false unless current_user_has_student_privileges?
load_request_for_authorization
return false unless @join_team_request
current_user_is_request_creator? || current_user_is_team_member?

when 'update', 'destroy'
# Only the participant who created the request can update or delete it
return false unless current_user_has_student_privileges?
load_request_for_authorization
return false unless @join_team_request
current_user_is_request_creator?

when 'decline', 'accept'
# Only team members of the target team can accept/decline a request
return false unless current_user_has_student_privileges?
load_request_for_authorization
return false unless @join_team_request
current_user_is_team_member?

when 'for_team', 'by_user', 'pending'
# Students can view filtered lists
current_user_has_student_privileges?

else
# Default: deny access
false
end
end

# GET /join_team_requests
# gets a list of all the join team requests
def index
unless @current_user.administrator?
return render json: { errors: 'Unauthorized' }, status: :unauthorized
end
join_team_requests = JoinTeamRequest.all
render json: join_team_requests, status: :ok
join_team_requests = JoinTeamRequest.includes(:participant, :team).all
render json: join_team_requests, each_serializer: JoinTeamRequestSerializer, status: :ok
end

# GET /join_team_requests/1
# GET /join_team_requests/1
# show the join team request that is passed into the route
def show
render json: @join_team_request, status: :ok
render json: @join_team_request, serializer: JoinTeamRequestSerializer, status: :ok
end

# GET /join_team_requests/for_team/:team_id
# Get all join team requests for a specific team
def for_team
team = Team.find(params[:team_id])
join_team_requests = team.join_team_requests.includes(:participant, :team)
render json: join_team_requests, each_serializer: JoinTeamRequestSerializer, status: :ok
rescue ActiveRecord::RecordNotFound
render json: { error: 'Team not found' }, status: :not_found
end

# GET /join_team_requests/by_user/:user_id
# Get all join team requests created by a specific user
def by_user
participant_ids = Participant.where(user_id: params[:user_id]).pluck(:id)
join_team_requests = JoinTeamRequest.where(participant_id: participant_ids).includes(:participant, :team)
render json: join_team_requests, each_serializer: JoinTeamRequestSerializer, status: :ok
end

# GET /join_team_requests/pending
# Get all pending join team requests
def pending
join_team_requests = JoinTeamRequest.where(reply_status: PENDING).includes(:participant, :team)
render json: join_team_requests, each_serializer: JoinTeamRequestSerializer, status: :ok
end

# POST /join_team_requests
# Creates a new join team request
def create
join_team_request = JoinTeamRequest.new
join_team_request.comments = params[:comments]
join_team_request.status = PENDING
join_team_request.team_id = params[:team_id]
participant = Participant.where(user_id: @current_user.id, assignment_id: params[:assignment_id]).first
team = Team.find(params[:team_id])
# Find participant object for the user who is requesting to join the team
participant = AssignmentParticipant.find_by(user_id: @current_user.id, parent_id: params[:assignment_id])

unless participant
return render json: { error: 'You are not a participant in this assignment' }, status: :unprocessable_entity
end

team = Team.find_by(id: params[:team_id])
unless team
return render json: { error: 'Team not found' }, status: :not_found
end

# Check if user already belongs to the team
if team.participants.include?(participant)
render json: { error: 'You already belong to the team' }, status: :unprocessable_entity
elsif participant
join_team_request.participant_id = participant.id
if join_team_request.save
render json: join_team_request, status: :created
else
render json: { errors: join_team_request.errors.full_messages }, status: :unprocessable_entity
end
return render json: { error: 'You already belong to this team' }, status: :unprocessable_entity
end

# Check for duplicate pending requests
existing_request = JoinTeamRequest.find_by(
participant_id: participant.id,
team_id: team.id,
reply_status: PENDING
)

if existing_request
return render json: { error: 'You already have a pending request to join this team' }, status: :unprocessable_entity
end

# Create the request
join_team_request = JoinTeamRequest.new(
participant_id: participant.id,
team_id: team.id,
comments: params[:comments],
reply_status: PENDING
)

if join_team_request.save
render json: join_team_request, serializer: JoinTeamRequestSerializer, status: :created
else
render json: { errors: 'Participant not found' }, status: :unprocessable_entity
render json: { errors: join_team_request.errors.full_messages }, status: :unprocessable_entity
end
rescue ActiveRecord::RecordNotFound => e
render json: { error: e.message }, status: :not_found
end

# PATCH/PUT /join_team_requests/1
# Updates a join team request
# Updates a join team request (comments only, not status)
def update
if @join_team_request.update(join_team_request_params)
render json: { message: 'JoinTeamRequest was successfully updated' }, status: :ok
# Only allow updating comments
if @join_team_request.update(comments: params[:comments])
render json: @join_team_request, serializer: JoinTeamRequestSerializer, status: :ok
else
render json: { errors: @join_team_request.errors.full_messages }, status: :unprocessable_entity
end
end

# DELETE /join_team_requests/1
# DELETE /join_team_requests/1
# delete a join team request
def destroy
if @join_team_request.destroy
render json: { message: 'JoinTeamRequest was successfully deleted' }, status: :ok
render json: { message: 'Join team request was successfully deleted' }, status: :ok
else
render json: { errors: 'Failed to delete JoinTeamRequest' }, status: :unprocessable_entity
render json: { error: 'Failed to delete join team request' }, status: :unprocessable_entity
end
end

# decline a join team request
# PATCH /join_team_requests/1/accept
# Accept a join team request and add the participant to the team
def accept
team = @join_team_request.team
if team.full?
return render json: { error: 'Team is full' }, status: :unprocessable_entity
end

participant = @join_team_request.participant

# Use a transaction to ensure both removal and addition happen atomically
ActiveRecord::Base.transaction do
# Find and remove participant from their old team (if any)
old_team_participant = TeamsParticipant.find_by(participant_id: participant.id)
if old_team_participant
old_team = old_team_participant.team
old_team_participant.destroy!

# If the old team is now empty, optionally clean up (but keep the team for now)
Rails.logger.info "Removed participant #{participant.id} from old team #{old_team&.id}"
end

# Add participant to the new team
TeamsParticipant.create!(
participant_id: participant.id,
team_id: team.id,
user_id: participant.user_id
)

# Update the request status
@join_team_request.update!(reply_status: ACCEPTED)

render json: {
message: 'Join team request accepted successfully',
join_team_request: JoinTeamRequestSerializer.new(@join_team_request).as_json
}, status: :ok
end
rescue ActiveRecord::RecordInvalid => e
render json: { error: e.message }, status: :unprocessable_entity
rescue StandardError => e
render json: { error: e.message }, status: :unprocessable_entity
end

# PATCH /join_team_requests/1/decline
# Decline a join team request
def decline
@join_team_request.status = DECLINED
if @join_team_request.save
render json: { message: 'JoinTeamRequest declined successfully' }, status: :ok
if @join_team_request.update(reply_status: DECLINED)
render json: {
message: 'Join team request declined successfully',
join_team_request: JoinTeamRequestSerializer.new(@join_team_request).as_json
}, status: :ok
else
render json: { errors: @join_team_request.errors.full_messages }, status: :unprocessable_entity
end
end

private

# checks if the team is full already
def check_team_status
team = Team.find(params[:team_id])
Expand All @@ -94,13 +230,47 @@ def check_team_status
end
end

# Ensures the request is still pending before processing accept/decline
def ensure_request_pending
unless @join_team_request.reply_status == PENDING
render json: { error: 'This request has already been processed' }, status: :unprocessable_entity
end
end

# Finds the join team request by ID
def find_request
@join_team_request = JoinTeamRequest.find(params[:id])
end

# Loads the request for authorization check (avoids duplicate queries)
def load_request_for_authorization
@join_team_request ||= JoinTeamRequest.find_by(id: params[:id])
end

# Permits specified parameters for join team requests
def join_team_request_params
params.require(:join_team_request).permit(:comments, :status)
params.require(:join_team_request).permit(:comments, :reply_status)
end

# Helper method to check if current user is the creator of the request
def current_user_is_request_creator?
return false unless @join_team_request && @current_user

@join_team_request.participant&.user_id == @current_user.id
end

# Helper method to check if current user is a member of the target team
def current_user_is_team_member?
return false unless @join_team_request && @current_user

team = @join_team_request.team
return false unless team.is_a?(AssignmentTeam)

participant = AssignmentParticipant.find_by(
user_id: @current_user.id,
parent_id: team.parent_id
)

participant && team.participants.include?(participant)
end
end
end
5 changes: 2 additions & 3 deletions app/controllers/participants_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,7 @@ def participant_params
# Filters participants based on the provided user
# Returns participants ordered by their IDs
def filter_user_participants(user)
participants = Participant.all
participants = participants.where(user_id: user.id) if user
participants = Participant.where(user_id: user.id) if user
participants.order(:id)
end

Expand Down Expand Up @@ -190,4 +189,4 @@ def validate_authorization

authorization
end
end
end
Loading