education/classroom

View on GitHub
app/controllers/assignment_invitations_controller.rb

Summary

Maintainability
A
1 hr
Test Coverage
# frozen_string_literal: true

class AssignmentInvitationsController < ApplicationController
  class InvalidStatusForRouteError < StandardError; end

  include InvitationsControllerMethods
  include RepoSetup

  before_action :route_based_on_status, only: %i[show setup success]
  before_action :check_should_redirect_to_roster_page, only: :show
  before_action :ensure_submission_repository_exists, only: :success

  # rubocop:disable MethodLength
  # rubocop:disable AbcSize
  def accept
    result = current_invitation.redeem_for(current_user)
    case result.status
    when :success
      current_invitation_status.completed! if current_invitation_status.unaccepted?
      GitHubClassroom.statsd.increment("exercise_invitation.accept")
    when :pending
      current_invitation_status.accepted!
      GitHubClassroom.statsd.increment("exercise_invitation.accept")
    when :failed
      GitHubClassroom.statsd.increment("exercise_invitation.fail")
      current_invitation_status.unaccepted!
      flash[:error] = result.error
    end
    route_based_on_status
  end
  # rubocop:enable MethodLength
  # rubocop:enable AbcSize

  def setup; end

  # rubocop:disable MethodLength
  # rubocop:disable AbcSize
  def create_repo
    job_started = false
    if current_invitation_status.accepted? || current_invitation_status.errored?
      assignment_repo = AssignmentRepo.find_by(assignment: current_assignment, user: current_user)
      assignment_repo&.destroy if assignment_repo&.github_repository&.empty?
      if current_invitation_status.errored_creating_repo?
        GitHubClassroom.statsd.increment("exercise_repo.create.retry")
      elsif current_invitation_status.errored_importing_starter_code?
        GitHubClassroom.statsd.increment("exercise_repo.import.retry")
      end
      current_invitation_status.waiting!
      CreateGitHubRepositoryNewJob.perform_later(current_assignment, current_user, retries: 3)

      job_started = true
    end
    render json: {
      job_started: job_started,
      status: current_invitation_status.status,
      repo_url: current_submission&.github_repository&.html_url
    }
  end
  # rubocop:enable MethodLength
  # rubocop:enable AbcSize

  def progress
    render json: {
      status: current_invitation_status.status,
      repo_url: current_submission&.github_repository&.html_url
    }
  end

  def show; end

  def success; end

  def join_roster
    super

    entry = organization.roster.roster_entries.find_by(user_id: current_user.id)
    flash[:success] = "Your account is linked to #{entry.identifier} on the roster. If this is wrong, please reach out to your instructor."

    redirect_to assignment_invitation_url(current_invitation)
  rescue ActiveRecord::ActiveRecordError
    flash[:error] = "An error occurred, please try again!"
  end

  private

  def ensure_submission_repository_exists
    github_repo_exists = current_submission &&
      current_submission
        .github_repository
        .present?(headers: GitHub::APIHeaders.no_cache_no_store)
    return if github_repo_exists

    current_submission&.destroy
    @current_submission = nil
    current_invitation_status.accepted!

    redirect_to setup_assignment_invitation_path
  end

  # rubocop:disable AbcSize
  # rubocop:disable CyclomaticComplexity
  def route_based_on_status
    case current_invitation_status.status
    when "unaccepted"
      redirect_to assignment_invitation_path(current_invitation) if action_name != "show"
    when "completed"
      redirect_to success_assignment_invitation_path if action_name != "success"
    when *(InviteStatus::ERRORED_STATUSES + InviteStatus::SETUP_STATUSES)
      redirect_to setup_assignment_invitation_path if action_name != "setup"
    else
      raise InvalidStatusForRouteError, "No route registered for status: #{current_invitation_status.status}"
    end
  end
  # rubocop:enable AbcSize
  # rubocop:enable CyclomaticComplexity

  def create_submission
    result = current_invitation.redeem_for(current_user)

    if result.success?
      yield if block_given?
    else
      GitHubClassroom.statsd.increment("exercise_invitation.fail")

      flash[:error] = result.error
      redirect_to assignment_invitation_path(current_invitation)
    end
  end

  def assignment
    @assignment ||= current_invitation.assignment
  end
  helper_method :assignment

  def current_submission
    @current_submission ||= AssignmentRepo.find_by(assignment: current_assignment, user: current_user)
  end

  def current_invitation
    @current_invitation ||= AssignmentInvitation.find_by!(key: params[:id])
  end

  def current_invitation_status
    @current_invitation_status ||= current_invitation.status(current_user)
  end

  def required_scopes
    GitHubClassroom::Scopes::ASSIGNMENT_STUDENT
  end
end