diff --git a/app/assets/javascripts/codeharbor_link.js b/app/assets/javascripts/codeharbor_link.js index 7ada3756..4cd49ece 100644 --- a/app/assets/javascripts/codeharbor_link.js +++ b/app/assets/javascripts/codeharbor_link.js @@ -28,17 +28,8 @@ $(document).on('turbolinks:load', function() { return replace(Array(32).join('x')); }); - - $('.generate-client-id').on('click', function () { - $('.client-id').val(generateUUID()); - }); - - $('.generate-client-secret').on('click', function () { - $('.client-secret').val(generateRandomHex32()); - }); - - $('.generate-oauth2-token').on('click', function () { - $('.oauth2-token').val(generateRandomHex32()) + $('.generate-api_key').on('click', function () { + $('.api_key').val(generateRandomHex32()) }); } } diff --git a/app/assets/javascripts/exercises.js.erb b/app/assets/javascripts/exercises.js.erb index 89e5cf3a..95b5539e 100644 --- a/app/assets/javascripts/exercises.js.erb +++ b/app/assets/javascripts/exercises.js.erb @@ -261,18 +261,16 @@ $(document).on('turbolinks:load', function() { var $messageDiv = $exerciseDiv.children('.export-message'); var $actionsDiv = $exerciseDiv.children('.export-exercise-actions'); - $messageDiv.html('requesting status'); - $actionsDiv.html('spinning'); + $messageDiv.removeClass('export-failure'); + + $messageDiv.html('<%= I18n.t('exercises.export_codeharbor.checking_codeharbor') %>'); + $actionsDiv.html('
'); return $.ajax({ type: 'POST', url: '/exercises/' + exerciseID + '/export_external_check', dataType: 'json', success: function(response) { - if (response.error) { - $messageDiv.html(response.error); - $actionsDiv.html('Retry?'); - } $messageDiv.html(response.message); return $actionsDiv.html(response.actions); }, @@ -380,7 +378,6 @@ $(document).on('turbolinks:load', function() { observeExecutionEnvironment(); observeUnpublishedState(); overrideTextareaTabBehavior(); - } else if ($('#files.jstree').isPresent()) { var fileTypeSelect = $('#code_ocean_file_file_type_id'); fileTypeSelect.on("change", function() {updateFileTemplates(fileTypeSelect.val())}); diff --git a/app/assets/stylesheets/exercises.css.scss b/app/assets/stylesheets/exercises.css.scss index 1208c846..e5118ffc 100644 --- a/app/assets/stylesheets/exercises.css.scss +++ b/app/assets/stylesheets/exercises.css.scss @@ -179,8 +179,11 @@ a.file-heading { #export-modal { .modal-content { - min-height: unset; - height: 300px; + min-height: 300px; + } + + .modal-body { + overflow: auto; } } diff --git a/app/controllers/codeharbor_links_controller.rb b/app/controllers/codeharbor_links_controller.rb index 732d897e..8c25df66 100644 --- a/app/controllers/codeharbor_links_controller.rb +++ b/app/controllers/codeharbor_links_controller.rb @@ -5,7 +5,8 @@ class CodeharborLinksController < ApplicationController before_action :set_codeharbor_link, only: %i[show edit update destroy] def new - @codeharbor_link = CodeharborLink.new + base_url = CodeOcean::Config.new(:code_ocean).read[:codeharbor][:url] + @codeharbor_link = CodeharborLink.new(push_url: base_url + '/import_exercise', check_uuid_url: base_url + '/import_uuid_check') authorize! end @@ -42,6 +43,6 @@ class CodeharborLinksController < ApplicationController end def codeharbor_link_params - params.require(:codeharbor_link).permit(:push_url, :oauth2token, :client_id, :client_secret) + params.require(:codeharbor_link).permit(:push_url, :check_uuid_url, :api_key) end end diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb index 17effa54..6df096fe 100644 --- a/app/controllers/exercises_controller.rb +++ b/app/controllers/exercises_controller.rb @@ -7,7 +7,7 @@ class ExercisesController < ApplicationController before_action :handle_file_uploads, only: [:create, :update] before_action :set_execution_environments, only: [:create, :edit, :new, :update] - before_action :set_exercise_and_authorize, only: MEMBER_ACTIONS + [:push_proforma_xml, :clone, :implement, :working_times, :intervention, :search, :run, :statistics, :submit, :reload, :feedback, :study_group_dashboard, :export_external_check, :export_external_confirm] + before_action :set_exercise_and_authorize, only: MEMBER_ACTIONS + [:clone, :implement, :working_times, :intervention, :search, :run, :statistics, :submit, :reload, :feedback, :study_group_dashboard, :export_external_check, :export_external_confirm] before_action :set_external_user_and_authorize, only: [:statistics] before_action :set_file_types, only: [:create, :edit, :new, :update] before_action :set_course_token, only: [:implement] @@ -107,54 +107,37 @@ class ExercisesController < ApplicationController @feedbacks = @exercise.user_exercise_feedbacks.paginate(page: params[:page]) end - def push_proforma_xml - error = ExerciseService::PushExternal.call( - zip: ProformaService::ExportTask.call(exercise: @exercise), - codeharbor_link: current_user.codeharbor_link - ) - if error.nil? - redirect_to exercises_path, notice: t('exercises.export_codeharbor.success') - else - redirect_to exercises_path, alert: t('exercises.export_codeharbor.fail') - end - end - def export_external_check @codeharbor_link = current_user.codeharbor_link - url = 'http://localhost:3001/import_uuid_check' - - conn = Faraday.new(url: url) do |faraday| + conn = Faraday.new(url: @codeharbor_link.check_uuid_url) do |faraday| faraday.options[:open_timeout] = 5 faraday.options[:timeout] = 5 faraday.adapter Faraday.default_adapter end - error = false - response_hash = {} - message = '' - begin - response = conn.post do |req| - req.headers['Content-Type'] = 'application/json' - req.headers['Authorization'] = 'Bearer ' + @codeharbor_link.api_key - req.body = {uuid: @exercise.uuid}.to_json - end - response_hash = JSON.parse(response.body, symbolize_names: true) - message = response_hash[:message] - rescue Faraday::Error => e - message = t('exercises.export_codeharbor.error', message: e.message) - error = true - end + codeharbor_check = begin + response = conn.post do |req| + req.headers['Content-Type'] = 'application/json' + req.headers['Authorization'] = 'Bearer ' + @codeharbor_link.api_key + req.body = {uuid: @exercise.uuid}.to_json + end + response_hash = JSON.parse(response.body, symbolize_names: true) + + {error: false}.merge(response_hash.slice(:message, :exercise_found, :update_right)) + rescue Faraday::Error => e + {error: true, message: t('exercises.export_codeharbor.error', message: e.message)} + end render json: { - message: message, + message: codeharbor_check[:message], actions: render_to_string( partial: 'export_actions', locals: { exercise: @exercise, - exercise_found: response_hash[:exercise_found], - update_right: response_hash[:update_right], - error: error, + exercise_found: codeharbor_check[:exercise_found], + update_right: codeharbor_check[:update_right], + error: codeharbor_check[:error], exported: false } ) @@ -179,15 +162,12 @@ class ExercisesController < ApplicationController message: t('exercises.export_codeharbor.successfully_exported', id: @exercise.id, title: @exercise.title), actions: render_to_string(partial: 'export_actions', locals: {exercise: @exercise, exported: true, error: error}) } - # @exercise, notice: t('controllers.exercise.push_external_notice', account_link: account_link.readable) else - # logger.debug(error) render json: { status: 'fail', message: t('exercises.export_codeharbor.export_failed', id: @exercise.id, title: @exercise.title, error: error), actions: render_to_string(partial: 'export_actions', locals: {exercise: @exercise, exported: true, error: error}) } - # redirect_to @exercise, alert: t('controllers.account_links.not_working', account_link: account_link.readable) end end diff --git a/app/models/codeharbor_link.rb b/app/models/codeharbor_link.rb index 18f2da02..b35dc6cf 100644 --- a/app/models/codeharbor_link.rb +++ b/app/models/codeharbor_link.rb @@ -1,11 +1,13 @@ # frozen_string_literal: true class CodeharborLink < ApplicationRecord - validates :oauth2token, presence: true + validates :push_url, presence: true + validates :check_uuid_url, presence: true + validates :api_key, presence: true belongs_to :user, foreign_key: :user_id, class_name: 'InternalUser' def to_s - oauth2token + id.to_s end end diff --git a/app/policies/exercise_policy.rb b/app/policies/exercise_policy.rb index 3d286271..ad305e11 100644 --- a/app/policies/exercise_policy.rb +++ b/app/policies/exercise_policy.rb @@ -7,7 +7,7 @@ class ExercisePolicy < AdminOrAuthorPolicy define_method(action) { admin? || teacher? } end - [:clone?, :destroy?, :edit?, :statistics?, :update?, :feedback?, :push_proforma_xml?, :export_external_check?, :export_external_confirm?].each do |action| + [:clone?, :destroy?, :edit?, :statistics?, :update?, :feedback?, :export_external_check?, :export_external_confirm?].each do |action| define_method(action) { admin? || author? } end diff --git a/app/services/exercise_service/push_external.rb b/app/services/exercise_service/push_external.rb index 3c72ac22..b0d8723f 100644 --- a/app/services/exercise_service/push_external.rb +++ b/app/services/exercise_service/push_external.rb @@ -2,7 +2,6 @@ module ExerciseService class PushExternal < ServiceBase - CODEHARBOR_PUSH_LINK = Rails.env.production? ? 'https://codeharbor.openhpi.de/import_exercise' : 'http://localhost:3001/import_exercise' def initialize(zip:, codeharbor_link:) @zip = zip @codeharbor_link = codeharbor_link @@ -11,7 +10,7 @@ module ExerciseService def execute body = @zip.string begin - conn = Faraday.new(url: CODEHARBOR_PUSH_LINK) do |faraday| + conn = Faraday.new(url: @codeharbor_link.push_url) do |faraday| faraday.adapter Faraday.default_adapter end diff --git a/app/views/codeharbor_links/_form.html.slim b/app/views/codeharbor_links/_form.html.slim index fac3c05c..a07afe1a 100644 --- a/app/views/codeharbor_links/_form.html.slim +++ b/app/views/codeharbor_links/_form.html.slim @@ -4,23 +4,14 @@ = f.label(:push_url) = f.text_field :push_url, data: {toggle: 'tooltip', placement: 'bottom'}, title: t('codeharbor_link.info.push_url'), class: 'form-control' .form-group - = f.label(:oauth2token) + = f.label(:check_uuid_url) + = f.text_field :check_uuid_url, data: {toggle: 'tooltip', placement: 'bottom'}, title: t('codeharbor_link.info.check_uuid_url'), class: 'form-control' + .form-group + = f.label(:api_key) .input-group - = f.text_field(:oauth2token, data: {toggle: 'tooltip', placement: 'bottom'}, title: t('codeharbor_link.info.token'), class: 'form-control oauth2-token') + = f.text_field(:api_key, data: {toggle: 'tooltip', placement: 'bottom'}, title: t('codeharbor_link.info.token'), class: 'form-control api_key') .input-group-btn - = button_tag t('codeharbor_link.generate'), type: 'button', class: 'generate-oauth2-token btn btn-default' - .field-element.form-group - = f.label(:client_id) - .input-group - = f.text_field(:client_id, data: {toggle: 'tooltip', placement: 'bottom'}, title: t('codeharbor_link.info.client_id'), class: 'form-control client-id') - .input-group-btn - = button_tag t('codeharbor_link.generate'), type: 'button', class: 'generate-client-id btn btn-default' - .field-element.form-group - = f.label(:client_secret) - .input-group - = f.text_field(:client_secret, data: {toggle: 'tooltip', placement: 'bottom'}, title: t('codeharbor_link.info.client_secret'), class: 'form-control client-secret') - .input-group-btn - = button_tag t('codeharbor_link.generate'), type: 'button', class: 'generate-client-secret btn btn-default' + = button_tag t('codeharbor_link.generate'), type: 'button', class: 'generate-api_key btn btn-default' .actions = render('shared/submit_button', f: f, object: @codeharbor_link) - if @codeharbor_link.persisted? diff --git a/app/views/exercises/index.html.slim b/app/views/exercises/index.html.slim index 22004850..30af9b9c 100644 --- a/app/views/exercises/index.html.slim +++ b/app/views/exercises/index.html.slim @@ -46,7 +46,7 @@ h1 = Exercise.model_name.human(count: 2) li = link_to(t('activerecord.models.user_exercise_feedback.other'), feedback_exercise_path(exercise), class: 'dropdown-item') if policy(exercise).feedback? li = link_to(t('shared.destroy'), exercise, data: {confirm: t('shared.confirm_destroy')}, method: :delete, class: 'dropdown-item') if policy(exercise).destroy? li = link_to(t('.clone'), clone_exercise_path(exercise), data: {confirm: t('shared.confirm_destroy')}, method: :post, class: 'dropdown-item') if policy(exercise).clone? - li = link_to(t('exercises.export_codeharbor.label'), '', class: 'dropdown-item export-start', data: {'exercise-id' => exercise.id}) if policy(exercise).push_proforma_xml? + li = link_to(t('exercises.export_codeharbor.label'), '', class: 'dropdown-item export-start', data: {'exercise-id' => exercise.id}) if policy(exercise).export_external_confirm? = render('shared/pagination', collection: @exercises) p = render('shared/new_button', model: Exercise) diff --git a/config/locales/en.yml b/config/locales/en.yml index 609f6550..fb9f2e55 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -246,7 +246,9 @@ en: generate: Generate info: push_url: | - The address on CodeHarbor side your exercise can be exported to. If you don't know what to write here, ask an admin. + The url from Codeharbor where your exercise can be exported to. If you don't know what to write here, ask an admin. + check_uuid_url: | + The url from Codeharbor where we can check if the exercise already exists. If you don't know what to write here, ask an admin. name: | Can be anything to Identify your account link. token: | @@ -323,13 +325,12 @@ en: exercise_found: A corresponding exercise has been found on Codeocean. You can either exercise_found_no_right: A corresponding exercise has been found on Codeocean, but you don't have the rights to edit it. You can only export_codeharbor: - fail: Failed to push the exercise to CodeHarbor. label: Export to Codeharbor - success: Successfully pushed the exercise to CodeHarbor. dialogtitle: Export to Codeharbor successfully_exported: 'Exercise has successfully been exported.
ID: %{id}
Title: %{title}' export_failed: 'Export has failed.
ID: %{id}
Title: %{title}

Error: %{error}' error: 'An error occurred while contacting Codeharbor
Error: %{message}' + checking_codeharbor: Checking whether exercise exists on Codeharbor. file_form: hints: feedback_message: This message is used as a hint for failing tests. diff --git a/config/routes.rb b/config/routes.rb index 6b744055..5f562661 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -85,7 +85,6 @@ Rails.application.routes.draw do get :reload post :submit get 'study_group_dashboard/:study_group_id', to: 'exercises#study_group_dashboard' - post :push_proforma_xml post :export_external_check post :export_external_confirm end diff --git a/db/migrate/20190818104954_add_push_url_rename_oauth2token_in_codeharbor_links.rb b/db/migrate/20190818104954_add_push_url_rename_oauth2token_in_codeharbor_links.rb index d3a3c01f..24dd9137 100644 --- a/db/migrate/20190818104954_add_push_url_rename_oauth2token_in_codeharbor_links.rb +++ b/db/migrate/20190818104954_add_push_url_rename_oauth2token_in_codeharbor_links.rb @@ -1,6 +1,7 @@ class AddPushUrlRenameOauth2tokenInCodeharborLinks < ActiveRecord::Migration[5.2] def change add_column :codeharbor_links, :push_url, :string + add_column :codeharbor_links, :check_uuid_url, :string rename_column :codeharbor_links, :oauth2token, :api_key end end diff --git a/db/schema.rb b/db/schema.rb index 6cd22c8f..fcd4e86b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -34,6 +34,7 @@ ActiveRecord::Schema.define(version: 2019_10_08_163045) do t.datetime "updated_at" t.integer "user_id" t.string "push_url" + t.string "check_uuid_url" t.index ["user_id"], name: "index_codeharbor_links_on_user_id" end