diff --git a/app/controllers/workflows_controller.rb b/app/controllers/workflows_controller.rb index 245e227144..1704c8ba86 100644 --- a/app/controllers/workflows_controller.rb +++ b/app/controllers/workflows_controller.rb @@ -355,7 +355,7 @@ def workflow_params { creator_ids: [] }, { assay_assets_attributes: [:assay_id] }, { publication_ids: [] }, { presentation_ids: [] }, { document_ids: [] }, { data_file_ids: [] }, { sop_ids: [] }, { workflow_data_files_attributes:[:id, :data_file_id, :workflow_data_file_relationship_id, :_destroy] }, - :internals, :maturity_level, :source_link_url, + :internals, :maturity_level, :source_link_url, :execution_instance_url, { topic_annotations: [] }, { operation_annotations: [] }, { discussion_links_attributes: [:id, :url, :label, :_destroy] }, { git_version_attributes: [:name, :comment, :ref, :commit, :root_path, diff --git a/app/helpers/workflows_helper.rb b/app/helpers/workflows_helper.rb index a447ff61e1..7136b79d5b 100644 --- a/app/helpers/workflows_helper.rb +++ b/app/helpers/workflows_helper.rb @@ -77,12 +77,6 @@ def test_status_badge(resource) end end - def run_workflow_url(workflow_version) - if workflow_version.workflow_class_title == 'Galaxy' - "#{Seek::Config.galaxy_instance_trs_import_url}&trs_id=#{workflow_version.parent.id}&trs_version=#{workflow_version.version}" - end - end - def workflow_class_options_for_select(selected = nil) opts = WorkflowClass.order(:title).map do |c| extra = {} diff --git a/app/models/asset_link.rb b/app/models/asset_link.rb index 3970f4866c..82b1e37b1b 100644 --- a/app/models/asset_link.rb +++ b/app/models/asset_link.rb @@ -1,6 +1,7 @@ class AssetLink < ApplicationRecord DISCUSSION = 'discussion'.freeze SOURCE = 'source'.freeze + EXECUTION_INSTANCE = 'execution_instance'.freeze MISC_LINKS = 'misc'.freeze scope :discussion, -> { where(link_type: AssetLink::DISCUSSION) } diff --git a/app/models/concerns/workflow_extraction.rb b/app/models/concerns/workflow_extraction.rb index f4c612b952..a9cf9ed027 100644 --- a/app/models/concerns/workflow_extraction.rb +++ b/app/models/concerns/workflow_extraction.rb @@ -58,7 +58,23 @@ def structure delegate :inputs, :outputs, :steps, to: :structure def can_run? - can_download?(nil) && workflow_class_title == 'Galaxy' && Seek::Config.galaxy_instance_trs_import_url.present? + can_download?(nil) && workflow_class&.executable? && run_url.present? + end + + def run_url + if workflow_class&.key == 'galaxy' + base = execution_instance_url || Seek::Config.galaxy_instance_default + return if base.nil? + + parent_id = is_a_version? ? parent.id : id + url = URI(base) + 'workflows/trs_import' + params = { + trs_url: Seek::Util.routes.ga4gh_trs_v2_tool_version_url(parent_id, version_id: version), + run_form: true + } + url.query = URI.encode_www_form(params) + url.to_s + end end def diagram_exists? diff --git a/app/models/git/blob.rb b/app/models/git/blob.rb index b8dcd7cbc9..5f91335ce3 100644 --- a/app/models/git/blob.rb +++ b/app/models/git/blob.rb @@ -10,12 +10,16 @@ class Blob delegate :read, :rewind, to: :file attr_reader :git_version, :path + # Flag to decide if `read`ing this blob should eagerly fetch any remote content pointed to by this blob's `url`. + attr_accessor :fetch_remote + alias_method :original_filename, :path def initialize(git_version, blob, path) @git_version = git_version @blob = blob @path = path + @fetch_remote = false end def annotations @@ -26,7 +30,7 @@ def url git_version.remote_sources[path] end - def file(fetch_remote: false) + def file(fetch_remote: @fetch_remote) @file ||= to_tempfile(fetch_remote: fetch_remote) end @@ -34,7 +38,7 @@ def binread file_contents end - def file_contents(as_text: false, fetch_remote: false, &block) + def file_contents(as_text: false, fetch_remote: @fetch_remote, &block) if fetch_remote && remote? && !fetched? if block_given? block.call(remote_content) @@ -92,13 +96,17 @@ def text_contents_for_search def remote_content return unless remote? - handler = ContentBlob.remote_content_handler_for(url) + handler = remote_content_handler return unless handler io = handler.fetch io.rewind io end + def remote_content_handler + ContentBlob.remote_content_handler_for(url) + end + def cache_key "#{git_repository.cache_key}/blobs/#{oid}" end @@ -134,7 +142,7 @@ def is_text? private - def to_tempfile(fetch_remote: false) + def to_tempfile(fetch_remote: @fetch_remote) f = Tempfile.new(path) f.binmode if binary? f << file_contents(as_text: !binary?, fetch_remote: fetch_remote) diff --git a/app/models/workflow.rb b/app/models/workflow.rb index 18bd0ff54b..eede3c2f6a 100644 --- a/app/models/workflow.rb +++ b/app/models/workflow.rb @@ -30,6 +30,9 @@ class Workflow < ApplicationRecord accepts_nested_attributes_for :workflow_data_files + has_one :execution_instance, -> { where(link_type: AssetLink::EXECUTION_INSTANCE) }, + class_name: 'AssetLink', as: :asset, dependent: :destroy, inverse_of: :asset, autosave: true + def initialize(*args) @extraction_errors = [] @extraction_warnings = [] @@ -131,6 +134,10 @@ def source_link_url parent&.source_link&.url end + def execution_instance_url + parent&.execution_instance&.url + end + def submit_to_life_monitor LifeMonitorSubmissionJob.perform_later(self) end @@ -237,6 +244,18 @@ def update_test_status(status, ver = version) v.save! end + def execution_instance_url= url + (execution_instance || build_execution_instance).assign_attributes(url: url) + + execution_instance.mark_for_destruction if url.blank? + + url + end + + def execution_instance_url + execution_instance&.url + end + has_filter maturity: Seek::Filtering::Filter.new( value_field: 'maturity_level', label_mapping: ->(values) { diff --git a/app/models/workflow_class.rb b/app/models/workflow_class.rb index 915b88c320..21507f2575 100644 --- a/app/models/workflow_class.rb +++ b/app/models/workflow_class.rb @@ -20,6 +20,10 @@ def extractable? extractor.present? end + def executable? + key == 'galaxy' + end + def self.extractable where.not(extractor: nil) end diff --git a/app/views/assets/_asset_buttons.html.erb b/app/views/assets/_asset_buttons.html.erb index fead830f7c..9d26ddbc7f 100644 --- a/app/views/assets/_asset_buttons.html.erb +++ b/app/views/assets/_asset_buttons.html.erb @@ -36,7 +36,7 @@ <%= button_link_to('Download RO Crate', 'ro_crate_file', ro_crate_workflow_path(asset, version: version, code: params[:code]), 'data-tooltip' => tooltip("The Workflow RO-Crate is a package containing the workflow definition, its metadata and supporting resources like test data")) %> <% if asset.can_run? %> - <%= button_link_to("Run on #{Seek::Config.galaxy_instance_name || 'Galaxy'}", 'run_galaxy', run_workflow_url(display_asset)) %> + <%= button_link_to("Run on Galaxy", 'run_galaxy', display_asset.run_url) %> <% end %> <% else %> <%= button_link_to('Download RO-Crate', 'ro_crate_file', nil, diff --git a/app/views/workflows/edit.html.erb b/app/views/workflows/edit.html.erb index 060296b5e2..ef6e2a52ae 100644 --- a/app/views/workflows/edit.html.erb +++ b/app/views/workflows/edit.html.erb @@ -18,6 +18,14 @@ <%= f.text_area :description, rows: 5, class: 'form-control rich-text-edit' -%> + <% if @workflow.workflow_class&.executable? %> +
+ + <%= f.text_field :execution_instance_url, placeholder: 'https://usegalaxy.eu/', class: 'form-control' -%> +

The root URL of the Galaxy instance where this workflow originated from.

+
+ <% end %> +
<%= f.text_field :source_link_url, placeholder: 'https://...', class: 'form-control' -%> diff --git a/app/views/workflows/provide_metadata.html.erb b/app/views/workflows/provide_metadata.html.erb index efa5bf2053..840f8371ee 100644 --- a/app/views/workflows/provide_metadata.html.erb +++ b/app/views/workflows/provide_metadata.html.erb @@ -45,6 +45,14 @@ <%= text_area_tag 'workflow[description]', @workflow.description, class: "form-control rich-text-edit" -%>
+ <% if @workflow.workflow_class&.executable? %> +
+ + <%= text_field_tag 'workflow[execution_instance_url]', @workflow.execution_instance_url, placeholder: 'https://usegalaxy.eu/', class: 'form-control' -%> +

The root URL of the Galaxy instance where this workflow originated from.

+
+ <% end %> +
<%= text_field_tag 'workflow[source_link_url]', @workflow.source_link_url, placeholder: 'https://...', class: "form-control" -%> diff --git a/config/initializers/seek_testing.rb b/config/initializers/seek_testing.rb index 0d41997c13..c2abfb51bb 100644 --- a/config/initializers/seek_testing.rb +++ b/config/initializers/seek_testing.rb @@ -139,5 +139,6 @@ def load_seek_testing_defaults! Settings.defaults[:git_support_enabled] = true Settings.defaults[:fair_signposting_enabled] = true Settings.defaults[:bio_tools_enabled] = true + Settings.defaults[:galaxy_instance_default] = 'https://usegalaxy.eu' end end diff --git a/config/routes.rb b/config/routes.rb index d0a3054d4c..67aa9cd4e9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -16,7 +16,7 @@ get 'tools' => 'tools#index' get 'tools/:id' => 'tools#show' get 'tools/:id/versions' => 'tool_versions#index' - get 'tools/:id/versions/:version_id' => 'tool_versions#show' + get 'tools/:id/versions/:version_id' => 'tool_versions#show', as: :tool_version get 'tools/:id/versions/:version_id/containerfile' => 'tool_versions#containerfile' get 'tools/:id/versions/:version_id/:type/descriptor(/*relative_path)' => 'tool_versions#descriptor', constraints: { relative_path: /.+/ }, format: false, as: :tool_versions_descriptor get 'tools/:id/versions/:version_id/:type/files' => 'tool_versions#files', format: false diff --git a/lib/seek/config_setting_attributes.yml b/lib/seek/config_setting_attributes.yml index 6cff135b3f..a026837c8b 100644 --- a/lib/seek/config_setting_attributes.yml +++ b/lib/seek/config_setting_attributes.yml @@ -240,8 +240,8 @@ life_monitor_url: life_monitor_client_id: life_monitor_client_secret: life_monitor_ui_url: -galaxy_instance_name: -galaxy_instance_trs_import_url: +galaxy_instance_default: +galaxy_instance_trs_server: # Controlled vocabs cv_dropdown_limit: convert: :to_i diff --git a/lib/seek/download_handling/galaxy_http_handler.rb b/lib/seek/download_handling/galaxy_http_handler.rb index 52cd276e72..d8ecfbc612 100644 --- a/lib/seek/download_handling/galaxy_http_handler.rb +++ b/lib/seek/download_handling/galaxy_http_handler.rb @@ -10,11 +10,20 @@ module DownloadHandling class GalaxyHTTPHandler < Seek::DownloadHandling::HTTPHandler attr_reader :galaxy_host, :workflow_id - def initialize(url, fallback_to_get: true) - uri = URI(url) + URL_PATTERNS = [ + /(.+)\/api\/workflows\/([^\/]+)\/download\?format=json-download/, # Download + /(.+)\/workflows\/run\?id=([^&]+)/, # Run + /(.+)\/published\/workflow\?id=([^&]+)/, # View + ].freeze - @galaxy_host = URI(url.split(/\/workflows?\//).first + '/') - @workflow_id = CGI.parse(uri.query)['id'].first + def initialize(url, fallback_to_get: true) + URL_PATTERNS.each do |pattern| + matches = url.match(pattern) + if matches + @galaxy_host = matches[1].chomp('/') + '/' + @workflow_id = matches[2] + end + end super(download_url, fallback_to_get: fallback_to_get) end @@ -26,21 +35,24 @@ def info end def display_url - URI.join(galaxy_host, "workflow/display_by_id?id=#{workflow_id}").to_s + URI.join(galaxy_host, "published/workflow?id=#{workflow_id}").to_s end def download_url - URI.join(galaxy_host, "workflow/export_to_file?id=#{workflow_id}").to_s + URI.join(galaxy_host, "api/workflows/#{workflow_id}/download?format=json-download").to_s end - # Note that the path is `/workflows/` (plural) here for some reason. def run_url URI.join(galaxy_host, "workflows/run?id=#{workflow_id}").to_s end + def execution_instance_url + galaxy_host.to_s + end + def self.is_galaxy_workflow_url?(uri) - uri.hostname.include?('galaxy') && (uri.path.include?('/workflow/') || uri.path.include?('/workflows/')) && - uri.query.present? && CGI.parse(uri.query)&.key?('id') + string_uri = uri.to_s + uri.hostname.include?('galaxy') && URL_PATTERNS.any? { |pattern| string_uri.match?(pattern) } end end end diff --git a/lib/seek/workflow_extractors/base.rb b/lib/seek/workflow_extractors/base.rb index f4adf4177e..7d0b14fc91 100644 --- a/lib/seek/workflow_extractors/base.rb +++ b/lib/seek/workflow_extractors/base.rb @@ -12,7 +12,13 @@ def initialize(io) end def metadata - { } + m = {} + + if @io.respond_to?(:remote_content_handler) + m.merge!(extract_source_metadata(@io.remote_content_handler)) + end + + m end def has_tests? @@ -84,6 +90,24 @@ def extract_author(obj) author end + + def extract_source_metadata(handler) + m = {} + return m unless handler + source_url = nil + if handler.respond_to?(:repository_url) + source_url = handler.repository_url + elsif handler.respond_to?(:display_url) + source_url = handler.display_url + end + m[:source_link_url] = source_url + + if handler.respond_to?(:execution_instance_url) + m[:execution_instance_url] = handler.execution_instance_url + end + + m + end end end end diff --git a/lib/seek/workflow_extractors/cff.rb b/lib/seek/workflow_extractors/cff.rb index 1312b2911b..271efdf2a8 100644 --- a/lib/seek/workflow_extractors/cff.rb +++ b/lib/seek/workflow_extractors/cff.rb @@ -6,7 +6,7 @@ class CFF FILENAME = 'CITATION.cff' def initialize(io) - if io.respond_to?(:path) + if io.respond_to?(:path) && !io.is_a?(Git::Blob) @path = io.path else f = Tempfile.new('cff') diff --git a/lib/seek/workflow_extractors/galaxy.rb b/lib/seek/workflow_extractors/galaxy.rb index aef35386c3..af0ccd3680 100644 --- a/lib/seek/workflow_extractors/galaxy.rb +++ b/lib/seek/workflow_extractors/galaxy.rb @@ -8,6 +8,7 @@ def self.file_extensions end def metadata + metadata = super galaxy_string = @io.read f = Tempfile.new('ga') f.binmode @@ -21,13 +22,13 @@ def metadata end cf.rewind if status.success? - metadata = Seek::WorkflowExtractors::CWL.new(cf).metadata + metadata.merge!(Seek::WorkflowExtractors::CWL.new(cf).metadata) else - metadata = super metadata[:warnings] ||= [] metadata[:warnings] << 'Unable to convert workflow to CWL, some metadata may be missing.' Rails.logger.error("Galaxy -> CWL conversion failed. Error was: #{err}") end + galaxy = JSON.parse(galaxy_string) if galaxy.has_key?('name') diff --git a/lib/seek/workflow_extractors/git_repo.rb b/lib/seek/workflow_extractors/git_repo.rb index 41370140d8..b5f0d1d472 100644 --- a/lib/seek/workflow_extractors/git_repo.rb +++ b/lib/seek/workflow_extractors/git_repo.rb @@ -32,7 +32,7 @@ def file_exists?(path) end def file(path) - @obj.get_blob(path)&.file(fetch_remote: true) + @obj.get_blob(path)&.tap { |blob| blob.fetch_remote = true } end def licensee_project diff --git a/lib/seek/workflow_extractors/knime.rb b/lib/seek/workflow_extractors/knime.rb index dfddac054b..6a15e00100 100644 --- a/lib/seek/workflow_extractors/knime.rb +++ b/lib/seek/workflow_extractors/knime.rb @@ -8,7 +8,7 @@ def self.file_extensions def metadata metadata = super - metadata.merge(parse_internal_workflow(extract_workflow)) + metadata.merge!(parse_internal_workflow(extract_workflow)) metadata end diff --git a/lib/seek/workflow_extractors/ro_crate.rb b/lib/seek/workflow_extractors/ro_crate.rb index 5f2567dfbc..33fbde69d4 100644 --- a/lib/seek/workflow_extractors/ro_crate.rb +++ b/lib/seek/workflow_extractors/ro_crate.rb @@ -105,16 +105,11 @@ def metadata_from_crate(crate, m) end end - source_url = crate['isBasedOn'] || crate['url'] || crate.main_workflow['url'] - if source_url - handler = ContentBlob.remote_content_handler_for(source_url) - if handler.respond_to?(:repository_url) - source_url = handler.repository_url - elsif handler.respond_to?(:display_url) - source_url = handler.display_url + source_url = crate['isBasedOn'] || crate['url'] || crate.main_workflow['url'] + if source_url + m.merge!(extract_source_metadata(ContentBlob.remote_content_handler_for(source_url))) + m[:source_link_url] ||= source_url # Use plain source URL if handler doesn't have something more appropriate end - m[:source_link_url] = source_url - end m end diff --git a/lib/seek/workflow_extractors/ro_like.rb b/lib/seek/workflow_extractors/ro_like.rb index 04bba108cd..65eefe744e 100644 --- a/lib/seek/workflow_extractors/ro_like.rb +++ b/lib/seek/workflow_extractors/ro_like.rb @@ -36,24 +36,27 @@ def generate_diagram end def metadata + m = super # Use CWL description - m = if abstract_cwl_extractor - begin - abstract_cwl_extractor.metadata - rescue StandardError => e - Rails.logger.error('Error extracting abstract CWL:') - Rails.logger.error(e) - { errors: ["Couldn't parse abstract CWL"] } - end - else - begin - main_workflow_extractor.metadata - rescue StandardError => e - Rails.logger.error('Error extracting workflow:') - Rails.logger.error(e) - { errors: ["Couldn't parse main workflow"] } - end - end + if abstract_cwl_extractor + begin + m.merge!(abstract_cwl_extractor.metadata) + rescue StandardError => e + Rails.logger.error('Error extracting abstract CWL:') + Rails.logger.error(e) + m[:errors] ||= [] + m[:errors] << "Couldn't parse abstract CWL" + end + else + begin + m.merge!(main_workflow_extractor.metadata) + rescue StandardError => e + Rails.logger.error('Error extracting workflow:') + Rails.logger.error(e) + m[:errors] ||= [] + m[:errors] << "Couldn't parse main workflow" + end + end if file_exists?('README.md') m[:description] ||= file('README.md').read.force_encoding('utf-8').gsub(/^(---\s*\n.*?\n?)^(---\s*$\n?)/m,'') # Remove "Front matter" diff --git a/test/functional/content_blobs_controller_test.rb b/test/functional/content_blobs_controller_test.rb index 5e9b8e32c1..da4b367bd6 100644 --- a/test/functional/content_blobs_controller_test.rb +++ b/test/functional/content_blobs_controller_test.rb @@ -94,7 +94,7 @@ def setup test 'examine url to galaxy instance works with the various workflow endpoints' do stub_request(:any, 'https://galaxy-instance.biz/banana/workflows/run?id=123').to_return(status: 200) - stub_request(:any, 'https://galaxy-instance.biz/banana/workflow/export_to_file?id=123').to_return( + stub_request(:any, 'https://galaxy-instance.biz/banana/api/workflows/123/download?format=json-download').to_return( body: File.new("#{Rails.root}/test/fixtures/files/workflows/1-PreProcessing.ga"), status: 200, headers: { 'Content-Length' => 40296, @@ -109,14 +109,14 @@ def setup assert_equal 'galaxy', assigns(:type) assert_equal '123', assigns(:info)[:workflow_id] assert_equal 'https://galaxy-instance.biz/banana/', assigns(:info)[:galaxy_host].to_s - assert_equal 'https://galaxy-instance.biz/banana/workflow/display_by_id?id=123', assigns(:info)[:display_url] + assert_equal 'https://galaxy-instance.biz/banana/published/workflow?id=123', assigns(:info)[:display_url] assert_equal 40296, assigns(:info)[:file_size] assert_equal '1-PreProcessing.ga', assigns(:info)[:file_name] } suite.call('https://galaxy-instance.biz/banana/workflows/run?id=123') - suite.call('https://galaxy-instance.biz/banana/workflow/export_to_file?id=123') - suite.call('https://galaxy-instance.biz/banana/workflow/display_by_id?id=123') + suite.call('https://galaxy-instance.biz/banana/api/workflows/123/download?format=json-download') + suite.call('https://galaxy-instance.biz/banana/published/workflow?id=123') end test 'examine url does not crash when examining galaxy-like URL' do diff --git a/test/functional/internationalization_test.rb b/test/functional/internationalization_error_test.rb similarity index 98% rename from test/functional/internationalization_test.rb rename to test/functional/internationalization_error_test.rb index 3979c810f7..27a4d093cd 100644 --- a/test/functional/internationalization_test.rb +++ b/test/functional/internationalization_error_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class InternationalizationTest < ActionController::TestCase +class InternationalizationErrorTest < ActionController::TestCase include AuthenticatedTestHelper diff --git a/test/functional/workflows_controller_test.rb b/test/functional/workflows_controller_test.rb index ce332e0750..845f75d45e 100644 --- a/test/functional/workflows_controller_test.rb +++ b/test/functional/workflows_controller_test.rb @@ -1775,4 +1775,28 @@ def bad_generator.write_graph(struct) assert flash[:error].include?('disabled') end end + + test 'shows run button for galaxy workflows using default galaxy endpoint' do + workflow = FactoryBot.create(:existing_galaxy_ro_crate_workflow, policy: FactoryBot.create(:public_policy)) + + get :show, params: { id: workflow.id } + + assert workflow.can_run? + assert_equal 'https://usegalaxy.eu', Seek::Config.galaxy_instance_default + trs_url = URI.encode_www_form_component("http://localhost:3000/ga4gh/trs/v2/tools/#{workflow.id}/versions/1") + assert_select 'a.btn[href=?]', "https://usegalaxy.eu/workflows/trs_import?trs_url=#{trs_url}&run_form=true", + { text: 'Run on Galaxy' } + end + + test 'shows run button for galaxy workflows using specified galaxy endpoint' do + workflow = FactoryBot.create(:existing_galaxy_ro_crate_workflow, policy: FactoryBot.create(:public_policy), + execution_instance_url: 'https://galaxygalaxy.org/mygalaxy/') + + get :show, params: { id: workflow.id } + + assert workflow.can_run? + trs_url = URI.encode_www_form_component("http://localhost:3000/ga4gh/trs/v2/tools/#{workflow.id}/versions/1") + assert_select 'a.btn[href=?]', "https://galaxygalaxy.org/mygalaxy/workflows/trs_import?trs_url=#{trs_url}&run_form=true", + { text: 'Run on Galaxy' } + end end diff --git a/test/integration/fair_signposting_test.rb b/test/integration/fair_signposting_test.rb index 8f85c788c6..af8ba74f17 100644 --- a/test/integration/fair_signposting_test.rb +++ b/test/integration/fair_signposting_test.rb @@ -106,7 +106,7 @@ class FairSignpostingTest < ActionDispatch::IntegrationTest end test 'fair signposting for home' do - get root_path(p) + get root_path assert_response :success links = parse_link_header @@ -133,7 +133,7 @@ class FairSignpostingTest < ActionDispatch::IntegrationTest end test 'fair signposting for privacy page' do - get privacy_home_path(p) + get privacy_home_path assert_response :success assert_nil response.headers['Link'], 'Should not have any signposting links' diff --git a/test/integration/git_workflow_creation_test.rb b/test/integration/git_workflow_creation_test.rb index 4c56ec0d72..4464baa93d 100644 --- a/test/integration/git_workflow_creation_test.rb +++ b/test/integration/git_workflow_creation_test.rb @@ -313,6 +313,87 @@ class GitWorkflowCreationTest < ActionDispatch::IntegrationTest assert_select '#extraction-errors ul li', text: /Couldn't parse main workflow/ end + test 'can extract source metadata using original URL for galaxy workflow' do + mock_remote_file "#{Rails.root}/test/fixtures/files/workflows/1-PreProcessing.ga", + 'http://galaxy.instance/api/workflows/abcdxyz/download?format=json-download', + { 'content-disposition' => 'attachment; filename="1-PreProcessing.ga"'} + + repo_count = Git::Repository.count + workflow_count = Workflow.count + version_count = Git::Version.count + annotation_count = Git::Annotation.count + + person = FactoryBot.create(:person) + galaxy = WorkflowClass.find_by_key('galaxy') || FactoryBot.create(:galaxy_workflow_class) + login_as(person.user) + + get new_workflow_path + + assert_enqueued_jobs(0) do + assert_difference('Git::Repository.count', 1) do + assert_no_difference('Task.count') do + post create_from_files_workflows_path, params: { + ro_crate: { + main_workflow: { data_url: 'http://galaxy.instance/workflows/run?id=abcdxyz' }, + }, + workflow_class_id: galaxy.id + } # Should go to metadata page... + end + end + end + + repo = assigns(:workflow).git_version.git_repository + assert_select 'input[name="workflow[title]"]', count: 1 + a = assigns(:workflow).git_version.remote_source_annotations + assert_equal 1, a.length + assert_equal 'http://galaxy.instance/workflows/run?id=abcdxyz', a.first.value + assert_equal '1-PreProcessing.ga', a.first.path + assert_equal 'http://galaxy.instance/', assigns(:workflow).execution_instance_url + assert_select '#workflow_execution_instance_url[value=?]', 'http://galaxy.instance/' + + assert_difference('Workflow.count', 1) do + assert_difference('Git::Version.count', 1) do + # 2 annotations = Main WF path, 1x remote source + assert_difference('Git::Annotation.count', 2) do + post create_metadata_workflows_path, params: { + workflow: { + workflow_class_id: galaxy.id, + title: 'blabla', + execution_instance_url: 'http://galaxy.instance/', + project_ids: [person.projects.first.id], + git_version_attributes: { + root_path: '/', + git_repository_id: repo.id, + ref: 'refs/heads/master', + main_workflow_path: '1-PreProcessing.ga', + remote_sources: { + '1-PreProcessing.ga' => 'http://galaxy.instance/workflows/run?id=abcdxyz' + } + } + } + } # Should go to workflow page... + end + end + end + + assert_redirected_to workflow_path(assigns(:workflow)) + + assert assigns(:workflow).latest_git_version.commit.present? + assert_equal 'refs/heads/master', assigns(:workflow).latest_git_version.ref + refute assigns(:workflow).latest_git_version.git_repository.remote? + assert_equal assigns(:workflow), assigns(:workflow).latest_git_version.git_repository.resource + assert assigns(:workflow).latest_git_version.get_blob('1-PreProcessing.ga').remote? + assert_equal({ '1-PreProcessing.ga' => 'http://galaxy.instance/workflows/run?id=abcdxyz' }, + assigns(:workflow).latest_git_version.remote_sources) + assert_equal 'http://galaxy.instance/', assigns(:workflow).latest_git_version.execution_instance_url + + # Check there wasn't anything extra created in the whole flow... + assert_equal repo_count + 1, Git::Repository.count + assert_equal workflow_count + 1, Workflow.count + assert_equal version_count + 1, Git::Version.count + assert_equal annotation_count + 2, Git::Annotation.count + end + private def login_as(user) diff --git a/test/integration/isa_exporter_test.rb b/test/integration/isa_exporter_compliance_test.rb similarity index 99% rename from test/integration/isa_exporter_test.rb rename to test/integration/isa_exporter_compliance_test.rb index 84c5a6662f..9981c94b22 100644 --- a/test/integration/isa_exporter_test.rb +++ b/test/integration/isa_exporter_compliance_test.rb @@ -2,7 +2,7 @@ require 'json' require 'json-schema' -class IsaExporterTest < ActionDispatch::IntegrationTest +class IsaExporterComplianceTest < ActionDispatch::IntegrationTest fixtures :all include SharingFormTestHelper diff --git a/test/unit/git/git_blob_test.rb b/test/unit/git/git_blob_test.rb index f906466fc5..b6d767c765 100644 --- a/test/unit/git/git_blob_test.rb +++ b/test/unit/git/git_blob_test.rb @@ -59,6 +59,21 @@ class GitBlobTest < ActiveSupport::TestCase remote_blob.file_contents(fetch_remote: true) do |c| assert_equal 'lit', c.read(3) end + + # fetch_remote + refute remote_blob.fetch_remote + assert_equal 0, remote_blob.size + assert_equal 0, remote_blob.file_contents.size + remote_blob.file_contents do |c| + assert_nil c.read(1) + end + + remote_blob.fetch_remote = true + assert remote_blob.fetch_remote + assert_equal 11, remote_blob.file_contents.size + remote_blob.file_contents do |c| + assert_equal 'lit', c.read(3) + end end test 'fetched remote blob' do diff --git a/test/unit/workflow_extraction/galaxy_extraction_test.rb b/test/unit/workflow_extraction/galaxy_extraction_test.rb index 42ad004569..0851b06431 100644 --- a/test/unit/workflow_extraction/galaxy_extraction_test.rb +++ b/test/unit/workflow_extraction/galaxy_extraction_test.rb @@ -74,4 +74,21 @@ class GalaxyExtractionTest < ActiveSupport::TestCase assert_equal [{ bio_tools_id: 'multiqc', name: 'MultiQC' }], metadata[:tools_attributes] end + + test 'extracts execution instance URL' do + mock_remote_file "#{Rails.root}/test/fixtures/files/workflows/1-PreProcessing.ga", + 'http://galaxy.instance/api/workflows/abcdxyz/download?format=json-download' + git_version = FactoryBot.create(:git_version) + disable_authorization_checks do + git_version.add_remote_file('1-PreProcessing.ga', 'http://galaxy.instance/published/workflow?id=abcdxyz') + git_version.fetch_remote_file('1-PreProcessing.ga') + git_version.save! + end + remote_blob = git_version.get_blob('1-PreProcessing.ga') + + extractor = Seek::WorkflowExtractors::Galaxy.new(remote_blob) + metadata = extractor.metadata + + assert_equal 'http://galaxy.instance/', metadata[:execution_instance_url] + end end diff --git a/test/unit/workflow_test.rb b/test/unit/workflow_test.rb index 94480af0f6..3bc0455299 100644 --- a/test/unit/workflow_test.rb +++ b/test/unit/workflow_test.rb @@ -116,7 +116,6 @@ class WorkflowTest < ActiveSupport::TestCase end assert_equal 'https://github.com/seek4science/cool-workflow', workflow.source_link_url - assert_equal 'https://github.com/seek4science/cool-workflow', workflow.source_link.url end test 'can clear source URL' do @@ -133,6 +132,7 @@ class WorkflowTest < ActiveSupport::TestCase end assert_nil workflow.reload.source_link + assert_nil workflow.reload.source_link_url end test 'generates RO-Crate and diagram for workflow/abstract workflow' do @@ -817,4 +817,72 @@ def bad_generator.write_graph(struct) assert_nil workflow.maturity_level end end + + test 'can get and set execution instance URL' do + workflow = FactoryBot.create(:workflow) + + assert_no_difference('AssetLink.count') do + workflow.execution_instance_url = 'https://mygalaxy.instance/' + end + + assert_difference('AssetLink.count', 1) do + disable_authorization_checks { workflow.save! } + end + + assert_equal 'https://mygalaxy.instance/', workflow.execution_instance_url + end + + test 'can clear execution instance URL' do + workflow = FactoryBot.create(:workflow, execution_instance_url: 'https://mygalaxy.instance/') + assert workflow.execution_instance + assert workflow.execution_instance_url + + assert_no_difference('AssetLink.count') do + workflow.execution_instance_url = nil + end + + assert_difference('AssetLink.count', -1) do + disable_authorization_checks { workflow.save! } + end + + assert_nil workflow.reload.execution_instance + assert_nil workflow.reload.execution_instance_url + end + + test 'can_run?' do + assert FactoryBot.create(:generated_galaxy_ro_crate_workflow, policy: FactoryBot.create(:public_policy)).can_run? + assert FactoryBot.create(:generated_galaxy_ro_crate_workflow, policy: FactoryBot.create(:public_policy), execution_instance_url: 'https://mygalaxy.instance/').can_run? + refute FactoryBot.create(:generated_galaxy_ro_crate_workflow, policy: FactoryBot.create(:private_policy)).can_run? + refute FactoryBot.create(:cwl_workflow, policy: FactoryBot.create(:public_policy)).can_run? + end + + test 'run_url' do + # Using default + with_config_value(:galaxy_instance_default, 'http://default-galaxy-instance.com') do + default = FactoryBot.create(:generated_galaxy_ro_crate_workflow) + trs_url = URI.encode_www_form_component("http://localhost:3000/ga4gh/trs/v2/tools/#{default.id}/versions/1") + assert_equal "http://default-galaxy-instance.com/workflows/trs_import?trs_url=#{trs_url}&run_form=true", default.run_url + end + + # With explicit execution instance + workflow = FactoryBot.create(:generated_galaxy_ro_crate_workflow, execution_instance_url: 'https://mygalaxy.instance/') + trs_url = URI.encode_www_form_component("http://localhost:3000/ga4gh/trs/v2/tools/#{workflow.id}/versions/1") + assert_equal "https://mygalaxy.instance/workflows/trs_import?trs_url=#{trs_url}&run_form=true", workflow.run_url + + # Versions + disable_authorization_checks do + workflow.save_as_new_version('new version') + end + v2_trs_url = URI.encode_www_form_component("http://localhost:3000/ga4gh/trs/v2/tools/#{workflow.id}/versions/2") + assert_equal "https://mygalaxy.instance/workflows/trs_import?trs_url=#{v2_trs_url}&run_form=true", workflow.run_url + assert_equal "https://mygalaxy.instance/workflows/trs_import?trs_url=#{trs_url}&run_form=true", workflow.find_version(1).run_url + + # Galaxy instance with sub-URI + workflow = FactoryBot.create(:generated_galaxy_ro_crate_workflow, execution_instance_url: 'https://mygalaxy.instance/galaxy/') + trs_url = URI.encode_www_form_component("http://localhost:3000/ga4gh/trs/v2/tools/#{workflow.id}/versions/1") + assert_equal "https://mygalaxy.instance/galaxy/workflows/trs_import?trs_url=#{trs_url}&run_form=true", workflow.run_url + + # Not supported for non-galaxy currently + assert_nil FactoryBot.create(:cwl_workflow, policy: FactoryBot.create(:public_policy)).run_url + end end