Spaces:
Build error
Build error
| class SubmissionsController < ApplicationController | |
| before_action :authorize_request, only: [:index, :destroy] | |
| before_action :check_maintenance, only: [:create, :destroy] | |
| before_action :check_wait, only: [:create] # Wait in batch_create is not allowed | |
| before_action :check_batched_submissions, only: [:batch_create, :batch_show] | |
| before_action :check_queue_size, only: [:create, :batch_create] | |
| before_action :check_requested_fields, except: [:batch_create] # Fields are ignored in batch_create | |
| before_action :set_base64_encoded | |
| def index | |
| page = params[:page].try(:to_i) || 1 | |
| per_page = params[:per_page].try(:to_i) || Submission.per_page | |
| if page <= 0 | |
| render json: { error: "invalid page: #{page}" }, status: :bad_request | |
| return | |
| elsif per_page < 0 | |
| render json: { error: "invalid per_page: #{per_page}" }, status: :bad_request | |
| return | |
| end | |
| submissions = Submission.paginate(page: page, per_page: per_page) | |
| serializable_submissions = ActiveModelSerializers::SerializableResource.new( | |
| submissions, { each_serializer: SubmissionSerializer, base64_encoded: @base64_encoded, fields: @requested_fields } | |
| ) | |
| render json: { | |
| submissions: serializable_submissions.as_json, | |
| meta: pagination_dict(submissions) | |
| } | |
| rescue Encoding::UndefinedConversionError => e | |
| render json: { | |
| error: "some attributes for one or more submissions cannot be converted to UTF-8, use base64_encoded=true query parameter" | |
| }, status: :bad_request | |
| end | |
| def destroy | |
| if !Config::ENABLE_SUBMISSION_DELETE | |
| render json: { error: "delete not allowed" }, status: :bad_request | |
| return | |
| end | |
| submission = Submission.find_by!(token: params[:token]) | |
| if submission.status == Status.queue || submission.status == Status.process | |
| render json: { | |
| error: "submission cannot be deleted because its status is #{submission.status.id} (#{submission.status.name})" | |
| }, status: :bad_request | |
| return | |
| end | |
| submission.delete | |
| # Forcing base64_encoded=true because it guarantees user will get requested data after delete. | |
| render json: submission, base64_encoded: true, fields: @requested_fields | |
| end | |
| def show | |
| token = params[:token] | |
| render json: Rails.cache.fetch("#{token}", expires_in: Config::SUBMISSION_CACHE_DURATION, race_condition_ttl: 0.1*Config::SUBMISSION_CACHE_DURATION) { | |
| Submission.find_by!(token: token) | |
| }, base64_encoded: @base64_encoded, fields: @requested_fields | |
| rescue Encoding::UndefinedConversionError | |
| render_conversion_error(:bad_request) | |
| end | |
| def batch_show | |
| tokens = (request.headers[:tokens] || params[:tokens]).to_s.strip.split(",") | |
| if tokens.length > Config::MAX_SUBMISSION_BATCH_SIZE | |
| render json: { | |
| error: "number of submissions in a batch should be less than or equal to #{Config::MAX_SUBMISSION_BATCH_SIZE}" | |
| }, status: :bad_request | |
| return | |
| elsif tokens.length == 0 | |
| render json: { | |
| error: "there should be at least one submission in a batch" | |
| }, status: :bad_request | |
| return | |
| end | |
| existing_submissions = Hash[Submission.where(token: tokens).collect{ |s| [s.token, s] }] | |
| submissions = [] | |
| tokens.each do |token| | |
| if existing_submissions.has_key?(token) | |
| serialized_submission = ActiveModelSerializers::SerializableResource.new( | |
| existing_submissions[token], { serializer: SubmissionSerializer, base64_encoded: @base64_encoded, fields: @requested_fields } | |
| ) | |
| submissions << serialized_submission.as_json | |
| else | |
| submissions << nil | |
| end | |
| end | |
| render json: { submissions: submissions } | |
| rescue Encoding::UndefinedConversionError => e | |
| render json: { | |
| error: "some attributes for one or more submissions cannot be converted to UTF-8, use base64_encoded=true query parameter" | |
| }, status: :bad_request | |
| end | |
| def create | |
| submission = Submission.new(submission_params(params)) | |
| if submission.save | |
| if @wait | |
| begin | |
| IsolateRunner.perform_now(submission) | |
| submission.reload | |
| render json: submission, status: :created, base64_encoded: @base64_encoded, fields: @requested_fields | |
| rescue Encoding::UndefinedConversionError => e | |
| render_conversion_error(:created, submission.token) | |
| end | |
| else | |
| IsolateRunner.perform_later(submission) | |
| render json: submission, status: :created, fields: [:token] | |
| end | |
| else | |
| render json: submission.errors, status: :unprocessable_entity | |
| end | |
| end | |
| # Batch Create does not support sync (wait=true) mode. | |
| def batch_create | |
| number_of_submissions = params[:submissions].try(:size).to_i | |
| if number_of_submissions > Config::MAX_SUBMISSION_BATCH_SIZE | |
| render json: { | |
| error: "number of submissions in a batch should be less than or equal to #{Config::MAX_SUBMISSION_BATCH_SIZE}" | |
| }, status: :bad_request | |
| return | |
| elsif number_of_submissions == 0 | |
| render json: { | |
| error: "there should be at least one submission in a batch" | |
| }, status: :bad_request | |
| return | |
| end | |
| submissions = params[:submissions].each.collect{ |p| Submission.new(submission_params(p)) } | |
| response = [] | |
| has_valid_submission = false | |
| submissions.each do |submission| | |
| if submission.save | |
| IsolateRunner.perform_later(submission) | |
| response << { token: submission.token } | |
| has_valid_submission = true | |
| else | |
| response << submission.errors | |
| end | |
| end | |
| render json: response, status: has_valid_submission ? :created : :unprocessable_entity | |
| end | |
| private | |
| def submission_params(params) | |
| submission_params = params.permit( | |
| :source_code, | |
| :language_id, | |
| :compiler_options, | |
| :command_line_arguments, | |
| :number_of_runs, | |
| :stdin, | |
| :expected_output, | |
| :cpu_time_limit, | |
| :cpu_extra_time, | |
| :wall_time_limit, | |
| :memory_limit, | |
| :stack_limit, | |
| :max_processes_and_or_threads, | |
| :enable_per_process_and_thread_time_limit, | |
| :enable_per_process_and_thread_memory_limit, | |
| :max_file_size, | |
| :redirect_stderr_to_stdout, | |
| :callback_url, | |
| :additional_files, | |
| :enable_network | |
| ) | |
| submission_params[:additional_files] = Base64Service.decode(submission_params[:additional_files]) | |
| if @base64_encoded | |
| submission_params[:source_code] = Base64Service.decode(submission_params[:source_code]) | |
| submission_params[:stdin] = Base64Service.decode(submission_params[:stdin]) | |
| submission_params[:expected_output] = Base64Service.decode(submission_params[:expected_output]) | |
| end | |
| submission_params | |
| end | |
| def check_wait | |
| @wait = params[:wait] == "true" | |
| if @wait && !Config::ENABLE_WAIT_RESULT | |
| render json: { error: "wait not allowed" }, status: :bad_request | |
| end | |
| end | |
| def check_batched_submissions | |
| unless Config::ENABLE_BATCHED_SUBMISSIONS | |
| render json: { error: "batched submissions are not allowed" }, status: :bad_request | |
| end | |
| end | |
| def check_queue_size | |
| number_of_submissions = params[:submissions].try(:size).presence || 1 | |
| if Resque.size(ENV["JUDGE0_VERSION"]) + number_of_submissions > Config::MAX_QUEUE_SIZE | |
| render json: { error: "queue is full" }, status: :service_unavailable | |
| end | |
| end | |
| def check_requested_fields | |
| fields_service = Fields::Submission.new(params[:fields]) | |
| render json: { error: "invalid fields: [#{fields_service.invalid_fields.join(", ")}]" }, status: :bad_request if fields_service.has_invalid_fields? | |
| @requested_fields = fields_service.requested_fields | |
| end | |
| def set_base64_encoded | |
| if Config::DISABLE_IMPLICIT_BASE64_ENCODING | |
| @base64_encoded = params[:base64_encoded] == "true" | |
| else | |
| @base64_encoded = params[:base64_encoded] != "false" | |
| end | |
| end | |
| def render_conversion_error(status, token = nil) | |
| response_json = { | |
| error: "some attributes for this submission cannot be converted to UTF-8, use base64_encoded=true query parameter", | |
| } | |
| response_json[:token] = token if token | |
| render json: response_json, status: status | |
| end | |
| end | |