From f3648cdf8caf9122cfe28ecdaac586a5a17be1eb Mon Sep 17 00:00:00 2001 From: Alex Skrenchuk Date: Wed, 23 Jul 2025 17:03:52 -0700 Subject: [PATCH 1/6] integrate feature/optmize-link-explorer / pr25 from ontoportal-lirmm into ncbo codebase --- Gemfile.lock | 2 +- config/config.test.rb | 2 +- lib/ontologies_api_client/base.rb | 20 +++++++-- lib/ontologies_api_client/collection.rb | 9 ++-- lib/ontologies_api_client/link_explorer.rb | 37 +++++++++++++--- test/models/test_explore.rb | 50 ++++++++++++++++++++++ test/test_case.rb | 11 +++++ 7 files changed, 116 insertions(+), 15 deletions(-) create mode 100644 test/models/test_explore.rb diff --git a/Gemfile.lock b/Gemfile.lock index 4a01ba6..94cafd0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -100,7 +100,7 @@ GEM unicode-display_width (3.1.4) unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) - uri (1.0.2) + uri (1.0.3) PLATFORMS arm64-darwin-24 diff --git a/config/config.test.rb b/config/config.test.rb index 1a9395f..478758a 100644 --- a/config/config.test.rb +++ b/config/config.test.rb @@ -13,5 +13,5 @@ config.links_attr = 'links' config.purl_host = 'purl.bioontology.org' config.purl_prefix = 'https://purl.bioontology.org/ontology' - config.cache = false + config.cache = true end diff --git a/lib/ontologies_api_client/base.rb b/lib/ontologies_api_client/base.rb index ff4aba6..bd2a5d0 100644 --- a/lib/ontologies_api_client/base.rb +++ b/lib/ontologies_api_client/base.rb @@ -66,6 +66,13 @@ def type @type end + def self.explore(id) + path = self.respond_to?(:collection_path) ? collection_path : '' + id = "#{path}/#{id}" unless id.include?(path) + inst = self.new(values: {id: id}) + LinkedData::Client::LinkExplorer.new({}, inst) + end + ## # Retrieve a set of data using a link provided on an object # This instantiates an instance of this class and uses @@ -132,11 +139,16 @@ def create_attributes(attributes) attr_exists = self.public_methods(false).include?(attr) unless attr_exists self.class.class_eval do - define_method attr.to_sym do - instance_variable_get("@#{attr}") + unless method_defined?(attr.to_sym) + define_method attr.to_sym do + instance_variable_get("@#{attr}") + end end - define_method "#{attr}=" do |val| - instance_variable_set("@#{attr}", val) + + unless method_defined?("#{attr}=".to_sym) + define_method "#{attr}=" do |val| + instance_variable_set("@#{attr}", val) + end end end end diff --git a/lib/ontologies_api_client/collection.rb b/lib/ontologies_api_client/collection.rb index f0bb0f2..add8ce4 100644 --- a/lib/ontologies_api_client/collection.rb +++ b/lib/ontologies_api_client/collection.rb @@ -24,15 +24,16 @@ def method_missing(meth, *args, &block) ## # Get all top-level links for the API - def top_level_links - @top_level_links ||= HTTP.get(LinkedData::Client.settings.rest_url) + def top_level_links(link = LinkedData::Client.settings.rest_url) + @top_level_links ||= {} + @top_level_links[link] ||= HTTP.get(link) end - ## + # # Return a link given an object (with links) and a media type def uri_from_context(object, media_type) object.links.each do |type, link| - return link if link.media_type && link.media_type.downcase.eql?(media_type.downcase) + return link.dup if link.media_type && link.media_type.downcase.eql?(media_type.downcase) end end diff --git a/lib/ontologies_api_client/link_explorer.rb b/lib/ontologies_api_client/link_explorer.rb index cc4c328..e612ef9 100644 --- a/lib/ontologies_api_client/link_explorer.rb +++ b/lib/ontologies_api_client/link_explorer.rb @@ -11,11 +11,17 @@ def initialize(links, instance) @instance = instance end + def get(params = {}) + get_link(@instance.id, params) + end + def method_missing(meth, *args, &block) if combined_links.key?(meth.to_s) explore_link(meth, *args) elsif meth == :batch explore_link(args) + elsif !@instance.id.blank? + forward_explore(meth, *args) else super end @@ -43,10 +49,7 @@ def explore_link(*args) ids = link.map {|l| l.to_s} value_cls.where {|o| ids.include?(o.id)} else - url = replace_template_elements(link.to_s, replacements) - value_cls = LinkedData::Client::Base.class_for_type(link.media_type) - params[:include] ||= value_cls.attributes(full_attributes) - HTTP.get(url, params) + get_link(link, params, replacements, full_attributes) end end @@ -56,6 +59,30 @@ def combined_links private + def forward_explore(meth, *args) + sub_id = Array(args).find { |x| x.is_a?(String) } || '' + escaped_sub_id = Addressable::URI.encode_component(sub_id, Addressable::URI::CharacterClasses::UNRESERVED) + link = "#{@instance.id}/#{meth}/#{escaped_sub_id}".chomp('/') + @instance.id = link + LinkExplorer.new(@links, @instance) + end + + # def forward_explore(meth, *args) + # sub_id = Array(args).find { |x| x.is_a?(String) } || '' + # link = "#{@instance.id}/#{meth}/#{CGI.escape(sub_id)}".chomp('/') + # @instance.id = link + # LinkExplorer.new(@links, @instance) + # end + + def get_link(link, params, replacements = [], full_attributes = {}) + url = replace_template_elements(link.to_s, replacements) + if link.respond_to? :media_type + value_cls = LinkedData::Client::Base.class_for_type(link.media_type) + params[:include] ||= value_cls.attributes(full_attributes) + end + HTTP.get(url, params) + end + def replace_template_elements(url, values = []) return url if values.nil? || values.empty? @@ -95,4 +122,4 @@ def linkable_attributes end end end -end \ No newline at end of file +end diff --git a/test/models/test_explore.rb b/test/models/test_explore.rb new file mode 100644 index 0000000..931cf18 --- /dev/null +++ b/test/models/test_explore.rb @@ -0,0 +1,50 @@ +require_relative '../test_case' +require 'pry' + +module Models + def self.method_missing + binding.pry + end +end + +class LinkExploreTest < LinkedData::Client::TestCase + def test_explore + sub_direct_explore = LinkedData::Client::Models::Ontology.explore('MEDDRA') + .latest_submission + .get(include: 'all') + + sub_indirect_explore = LinkedData::Client::Models::Ontology.find('MEDDRA').explore.latest_submission + + refute_nil sub_direct_explore + refute_nil sub_indirect_explore + + sub_direct_explore.to_hash.each do |key, value| + value_to_compare = sub_indirect_explore.to_hash[key] + if value.class.ancestors.include?(LinkedData::Client::Base) + value = value.to_hash + value_to_compare = value_to_compare.to_hash + end + # assert_equal value_to_compare, value, "Mismatch for key #{key}" + assert value == value_to_compare, "Mismatch for key #{key}: #{value.inspect} != #{value_to_compare.inspect}" + end + end + + def test_explore_class + + id = 'http://purl.org/sig/ont/fma/fma62955' + cls = LinkedData::Client::Models::Ontology.explore('FMA') + .classes(id) + .children + .get + + refute_empty cls.collection + + cls = LinkedData::Client::Models::Ontology.explore('FMA') + .classes(id) + .children + .get(include: 'prefLabel') + + refute_empty cls.collection + end + +end diff --git a/test/test_case.rb b/test/test_case.rb index 3e5a855..9928f15 100644 --- a/test/test_case.rb +++ b/test/test_case.rb @@ -1,10 +1,21 @@ # frozen_string_literal: true +require 'logger' +require 'active_support' +require 'active_support/logger' + require 'minitest/autorun' require 'minitest/hooks/test' require_relative '../lib/ontologies_api_client' require_relative '../config/config' +Logger = ::Logger unless defined?(Logger) +module Rails + def self.cache + @cache ||= ActiveSupport::Cache::MemoryStore.new + end +end + module LinkedData module Client class TestCase < Minitest::Test From 1842dbf009c8026bdb272a479806b57e2d648cfe Mon Sep 17 00:00:00 2001 From: Alex Skrenchuk Date: Thu, 24 Jul 2025 14:51:40 -0700 Subject: [PATCH 2/6] add SKOS features Brings https://github.com/ontoportal-lirmm/ontologies_api_ruby_client/pull/7 into ncbo codebase --- lib/ontologies_api_client/models/class.rb | 6 +++--- lib/ontologies_api_client/models/collection.rb | 12 ++++++++++++ lib/ontologies_api_client/models/scheme.rb | 12 ++++++++++++ lib/ontologies_api_client/models/skos_xl_label.rb | 12 ++++++++++++ 4 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 lib/ontologies_api_client/models/collection.rb create mode 100644 lib/ontologies_api_client/models/scheme.rb create mode 100644 lib/ontologies_api_client/models/skos_xl_label.rb diff --git a/lib/ontologies_api_client/models/class.rb b/lib/ontologies_api_client/models/class.rb index 7c2e250..d98e7c8 100644 --- a/lib/ontologies_api_client/models/class.rb +++ b/lib/ontologies_api_client/models/class.rb @@ -8,9 +8,9 @@ module Models class Class < LinkedData::Client::Base HTTP = LinkedData::Client::HTTP @media_type = %w[http://www.w3.org/2002/07/owl#Class http://www.w3.org/2004/02/skos/core#Concept] - @include_attrs = "prefLabel,definition,synonym,obsolete,hasChildren" - @include_attrs_full = "prefLabel,definition,synonym,obsolete,properties,hasChildren,children" - @attrs_always_present = :prefLabel, :definition, :synonym, :obsolete, :properties, :hasChildren, :children + @include_attrs = "prefLabel,definition,synonym,obsolete,hasChildren,inScheme,memberOf" + @include_attrs_full = "prefLabel,definition,synonym,obsolete,properties,hasChildren,childre,inScheme,memberOf" + @attrs_always_present = :prefLabel, :definition, :synonym, :obsolete, :properties, :hasChildren, :children, :inScheme, :memberOf alias :fullId :id diff --git a/lib/ontologies_api_client/models/collection.rb b/lib/ontologies_api_client/models/collection.rb new file mode 100644 index 0000000..b4c2e01 --- /dev/null +++ b/lib/ontologies_api_client/models/collection.rb @@ -0,0 +1,12 @@ +require_relative "../base" + +module LinkedData + module Client + module Models + class Collection < LinkedData::Client::Base + include LinkedData::Client::Collection + @media_type = "http://www.w3.org/2004/02/skos/core#Collection" + end + end + end +end \ No newline at end of file diff --git a/lib/ontologies_api_client/models/scheme.rb b/lib/ontologies_api_client/models/scheme.rb new file mode 100644 index 0000000..a070a96 --- /dev/null +++ b/lib/ontologies_api_client/models/scheme.rb @@ -0,0 +1,12 @@ +require_relative "../base" + +module LinkedData + module Client + module Models + class Scheme < LinkedData::Client::Base + include LinkedData::Client::Collection + @media_type = "http://www.w3.org/2004/02/skos/core#ConceptScheme" + end + end + end +end \ No newline at end of file diff --git a/lib/ontologies_api_client/models/skos_xl_label.rb b/lib/ontologies_api_client/models/skos_xl_label.rb new file mode 100644 index 0000000..b209af4 --- /dev/null +++ b/lib/ontologies_api_client/models/skos_xl_label.rb @@ -0,0 +1,12 @@ +require_relative "../base" + +module LinkedData + module Client + module Models + class Label < LinkedData::Client::Base + include LinkedData::Client::Collection + @media_type = "http://www.w3.org/2008/05/skos-xl#Label" + end + end + end +end From 233a03284cfd722f885f63ae09d9310496f1d5f9 Mon Sep 17 00:00:00 2001 From: Alex Skrenchuk Date: Thu, 24 Jul 2025 21:06:24 -0700 Subject: [PATCH 3/6] clean up --- config/config.test.rb | 2 +- lib/ontologies_api_client/link_explorer.rb | 7 ------- test/test_case.rb | 11 ----------- 3 files changed, 1 insertion(+), 19 deletions(-) diff --git a/config/config.test.rb b/config/config.test.rb index 478758a..1a9395f 100644 --- a/config/config.test.rb +++ b/config/config.test.rb @@ -13,5 +13,5 @@ config.links_attr = 'links' config.purl_host = 'purl.bioontology.org' config.purl_prefix = 'https://purl.bioontology.org/ontology' - config.cache = true + config.cache = false end diff --git a/lib/ontologies_api_client/link_explorer.rb b/lib/ontologies_api_client/link_explorer.rb index e612ef9..db576cb 100644 --- a/lib/ontologies_api_client/link_explorer.rb +++ b/lib/ontologies_api_client/link_explorer.rb @@ -67,13 +67,6 @@ def forward_explore(meth, *args) LinkExplorer.new(@links, @instance) end - # def forward_explore(meth, *args) - # sub_id = Array(args).find { |x| x.is_a?(String) } || '' - # link = "#{@instance.id}/#{meth}/#{CGI.escape(sub_id)}".chomp('/') - # @instance.id = link - # LinkExplorer.new(@links, @instance) - # end - def get_link(link, params, replacements = [], full_attributes = {}) url = replace_template_elements(link.to_s, replacements) if link.respond_to? :media_type diff --git a/test/test_case.rb b/test/test_case.rb index 9928f15..3e5a855 100644 --- a/test/test_case.rb +++ b/test/test_case.rb @@ -1,21 +1,10 @@ # frozen_string_literal: true -require 'logger' -require 'active_support' -require 'active_support/logger' - require 'minitest/autorun' require 'minitest/hooks/test' require_relative '../lib/ontologies_api_client' require_relative '../config/config' -Logger = ::Logger unless defined?(Logger) -module Rails - def self.cache - @cache ||= ActiveSupport::Cache::MemoryStore.new - end -end - module LinkedData module Client class TestCase < Minitest::Test From c3be85bc434e5fc8d7f4f7968612c06c94153bfa Mon Sep 17 00:00:00 2001 From: Alex Skrenchuk Date: Mon, 28 Jul 2025 16:39:29 -0700 Subject: [PATCH 4/6] Avoid following PURL redirects and split PURL tests into a separate file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated PURL-related tests to assert only the HTTP 302 status and Location header, without following the redirect to the BioPortal UI. This avoids triggering Cloudflare bot protection during CI runs. We do not want to test UI behavior here — we only verify that the correct redirect is served by the PURL resolver. Testing how the UI handles or rewrites URLs (e.g., with query parameters or concept ID fragments) is out of scope for these tests. Split PURL resolution tests into a new file test_class_purl.rb to separate concerns from core class model tests in test_class.rb Resolves ncbo/ontologies_api_ruby_client/issues/41 --- Gemfile.lock | 2 +- test/models/test_class.rb | 55 ------------------------------- test/models/test_class_purl.rb | 59 ++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 56 deletions(-) create mode 100644 test/models/test_class_purl.rb diff --git a/Gemfile.lock b/Gemfile.lock index 4a01ba6..94cafd0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -100,7 +100,7 @@ GEM unicode-display_width (3.1.4) unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) - uri (1.0.2) + uri (1.0.3) PLATFORMS arm64-darwin-24 diff --git a/test/models/test_class.rb b/test/models/test_class.rb index 0229ffe..b6a07e5 100644 --- a/test/models/test_class.rb +++ b/test/models/test_class.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require 'faraday/follow_redirects' require_relative '../test_case' class ClassTest < LinkedData::Client::TestCase @@ -16,58 +15,4 @@ def test_find assert_equal ontology, cls.links['ontology'] assert cls.hasChildren end - - # Test PURL generation for a class in an OWL format ontology - def test_purl_owl - cls = LinkedData::Client::Models::Class.find( - 'http://bioontology.org/ontologies/Activity.owl#Activity', - 'https://data.bioontology.org/ontologies/BRO' - ) - refute_nil cls - - res = fetch_response(cls.purl) - assert_equal 200, res.status - assert_equal 'https://bioportal.bioontology.org/ontologies/BRO'\ - '?p=classes&conceptid=http%3A%2F%2Fbioontology.org%2Fontologies%2FActivity.owl%23Activity', - res.env[:url].to_s - end - - # Test PURL generation for a class in a UMLS format ontology - def test_purl_umls - cls = LinkedData::Client::Models::Class.find( - 'http://purl.bioontology.org/ontology/SNOMEDCT/64572001', - 'https://bioportal.bioontology.org/ontologies/SNOMEDCT' - ) - refute_nil cls - - res = fetch_response(cls.purl) - assert_equal 200, res.status - assert_equal 'https://bioportal.bioontology.org/ontologies/SNOMEDCT?p=classes&conceptid=64572001', - res.env[:url].to_s - end - - # Test PURL generation for a class in an OBO format ontology - def test_purl_obo - cls = LinkedData::Client::Models::Class.find( - 'http://purl.obolibrary.org/obo/DOID_4', - 'https://bioportal.bioontology.org/ontologies/DOID' - ) - refute_nil cls - - res = fetch_response(cls.purl) - assert_equal 200, res.status - assert_equal 'https://bioportal.bioontology.org/ontologies/DOID'\ - '?p=classes&conceptid=http%3A%2F%2Fpurl.obolibrary.org%2Fobo%2FDOID_4', - res.env[:url].to_s - end - - private - - def fetch_response(url) - conn = Faraday.new do |f| - f.response :follow_redirects - f.adapter Faraday.default_adapter - end - conn.get(url) - end end diff --git a/test/models/test_class_purl.rb b/test/models/test_class_purl.rb new file mode 100644 index 0000000..e9893f7 --- /dev/null +++ b/test/models/test_class_purl.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require_relative '../test_case' + +class ClassTest < LinkedData::Client::TestCase + + # Test PURL generation for a class in an OWL format ontology + def test_purl_owl + cls = LinkedData::Client::Models::Class.find( + 'http://bioontology.org/ontologies/Activity.owl#Activity', + 'https://data.bioontology.org/ontologies/BRO' + ) + refute_nil cls + + res = fetch_response(cls.purl) + assert_equal 302, res.status + assert_equal 'https://bioportal.bioontology.org/ontologies/BRO'\ + '/classes?conceptid=http%3A%2F%2Fbioontology.org%2Fontologies%2FActivity.owl%23Activity', + res.headers['location'] + end + + # Test PURL generation for a class in a UMLS format ontology + def test_purl_umls + cls = LinkedData::Client::Models::Class.find( + 'http://purl.bioontology.org/ontology/SNOMEDCT/64572001', + 'https://bioportal.bioontology.org/ontologies/SNOMEDCT' + ) + refute_nil cls + + res = fetch_response(cls.purl) + assert_equal 302, res.status + assert_equal 'https://bioportal.bioontology.org/ontologies/SNOMEDCT/classes/64572001', + res.headers['location'] + end + + # Test PURL generation for a class in an OBO format ontology + def test_purl_obo + cls = LinkedData::Client::Models::Class.find( + 'http://purl.obolibrary.org/obo/DOID_4', + 'https://bioportal.bioontology.org/ontologies/DOID' + ) + refute_nil cls + + res = fetch_response(cls.purl) + assert_equal 302, res.status + assert_equal 'https://bioportal.bioontology.org/ontologies/DOID'\ + '/classes?conceptid=http%3A%2F%2Fpurl.obolibrary.org%2Fobo%2FDOID_4', + res.headers['location'] + end + + private + + def fetch_response(url) + conn = Faraday.new do |f| + f.adapter Faraday.default_adapter + end + conn.get(url) + end +end From 0523e4f36e5e980ff8ef00d1562f017315d9bc71 Mon Sep 17 00:00:00 2001 From: Alex Skrenchuk Date: Mon, 28 Jul 2025 18:31:59 -0700 Subject: [PATCH 5/6] Revert "Avoid following PURL redirects and split PURL tests into a separate file" This reverts commit c3be85bc434e5fc8d7f4f7968612c06c94153bfa. --- Gemfile.lock | 2 +- test/models/test_class.rb | 55 +++++++++++++++++++++++++++++++ test/models/test_class_purl.rb | 59 ---------------------------------- 3 files changed, 56 insertions(+), 60 deletions(-) delete mode 100644 test/models/test_class_purl.rb diff --git a/Gemfile.lock b/Gemfile.lock index 94cafd0..4a01ba6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -100,7 +100,7 @@ GEM unicode-display_width (3.1.4) unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) - uri (1.0.3) + uri (1.0.2) PLATFORMS arm64-darwin-24 diff --git a/test/models/test_class.rb b/test/models/test_class.rb index b6a07e5..0229ffe 100644 --- a/test/models/test_class.rb +++ b/test/models/test_class.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'faraday/follow_redirects' require_relative '../test_case' class ClassTest < LinkedData::Client::TestCase @@ -15,4 +16,58 @@ def test_find assert_equal ontology, cls.links['ontology'] assert cls.hasChildren end + + # Test PURL generation for a class in an OWL format ontology + def test_purl_owl + cls = LinkedData::Client::Models::Class.find( + 'http://bioontology.org/ontologies/Activity.owl#Activity', + 'https://data.bioontology.org/ontologies/BRO' + ) + refute_nil cls + + res = fetch_response(cls.purl) + assert_equal 200, res.status + assert_equal 'https://bioportal.bioontology.org/ontologies/BRO'\ + '?p=classes&conceptid=http%3A%2F%2Fbioontology.org%2Fontologies%2FActivity.owl%23Activity', + res.env[:url].to_s + end + + # Test PURL generation for a class in a UMLS format ontology + def test_purl_umls + cls = LinkedData::Client::Models::Class.find( + 'http://purl.bioontology.org/ontology/SNOMEDCT/64572001', + 'https://bioportal.bioontology.org/ontologies/SNOMEDCT' + ) + refute_nil cls + + res = fetch_response(cls.purl) + assert_equal 200, res.status + assert_equal 'https://bioportal.bioontology.org/ontologies/SNOMEDCT?p=classes&conceptid=64572001', + res.env[:url].to_s + end + + # Test PURL generation for a class in an OBO format ontology + def test_purl_obo + cls = LinkedData::Client::Models::Class.find( + 'http://purl.obolibrary.org/obo/DOID_4', + 'https://bioportal.bioontology.org/ontologies/DOID' + ) + refute_nil cls + + res = fetch_response(cls.purl) + assert_equal 200, res.status + assert_equal 'https://bioportal.bioontology.org/ontologies/DOID'\ + '?p=classes&conceptid=http%3A%2F%2Fpurl.obolibrary.org%2Fobo%2FDOID_4', + res.env[:url].to_s + end + + private + + def fetch_response(url) + conn = Faraday.new do |f| + f.response :follow_redirects + f.adapter Faraday.default_adapter + end + conn.get(url) + end end diff --git a/test/models/test_class_purl.rb b/test/models/test_class_purl.rb deleted file mode 100644 index e9893f7..0000000 --- a/test/models/test_class_purl.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -require_relative '../test_case' - -class ClassTest < LinkedData::Client::TestCase - - # Test PURL generation for a class in an OWL format ontology - def test_purl_owl - cls = LinkedData::Client::Models::Class.find( - 'http://bioontology.org/ontologies/Activity.owl#Activity', - 'https://data.bioontology.org/ontologies/BRO' - ) - refute_nil cls - - res = fetch_response(cls.purl) - assert_equal 302, res.status - assert_equal 'https://bioportal.bioontology.org/ontologies/BRO'\ - '/classes?conceptid=http%3A%2F%2Fbioontology.org%2Fontologies%2FActivity.owl%23Activity', - res.headers['location'] - end - - # Test PURL generation for a class in a UMLS format ontology - def test_purl_umls - cls = LinkedData::Client::Models::Class.find( - 'http://purl.bioontology.org/ontology/SNOMEDCT/64572001', - 'https://bioportal.bioontology.org/ontologies/SNOMEDCT' - ) - refute_nil cls - - res = fetch_response(cls.purl) - assert_equal 302, res.status - assert_equal 'https://bioportal.bioontology.org/ontologies/SNOMEDCT/classes/64572001', - res.headers['location'] - end - - # Test PURL generation for a class in an OBO format ontology - def test_purl_obo - cls = LinkedData::Client::Models::Class.find( - 'http://purl.obolibrary.org/obo/DOID_4', - 'https://bioportal.bioontology.org/ontologies/DOID' - ) - refute_nil cls - - res = fetch_response(cls.purl) - assert_equal 302, res.status - assert_equal 'https://bioportal.bioontology.org/ontologies/DOID'\ - '/classes?conceptid=http%3A%2F%2Fpurl.obolibrary.org%2Fobo%2FDOID_4', - res.headers['location'] - end - - private - - def fetch_response(url) - conn = Faraday.new do |f| - f.adapter Faraday.default_adapter - end - conn.get(url) - end -end From 252954905aca1f433890e12ec8583ffc7898cc36 Mon Sep 17 00:00:00 2001 From: Alex Skrenchuk Date: Mon, 28 Jul 2025 20:40:09 -0700 Subject: [PATCH 6/6] test: assert PURL construction and redirect headers without following UI redirects Updated tests for the `.purl` method to: - Assert the constructed URL matches the expected output based on client config - Confirm the PURL resolves via HTTP 302 to the correct UI target - Avoid following the UI-level redirect to prevent Cloudflare issues in CI - Used `@@purl_prefix` from config.purl_prefix. --- Gemfile.lock | 2 +- test/models/test_class.rb | 37 +++++++++++++++++++++---------------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 4a01ba6..94cafd0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -100,7 +100,7 @@ GEM unicode-display_width (3.1.4) unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) - uri (1.0.2) + uri (1.0.3) PLATFORMS arm64-darwin-24 diff --git a/test/models/test_class.rb b/test/models/test_class.rb index 0229ffe..c0d6942 100644 --- a/test/models/test_class.rb +++ b/test/models/test_class.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true -require 'faraday/follow_redirects' require_relative '../test_case' class ClassTest < LinkedData::Client::TestCase + @@purl_prefix = LinkedData::Client.settings.purl_prefix + def test_find id = 'http://bioontology.org/ontologies/Activity.owl#Activity' ontology = 'https://data.bioontology.org/ontologies/BRO' @@ -24,12 +25,13 @@ def test_purl_owl 'https://data.bioontology.org/ontologies/BRO' ) refute_nil cls + expected_purl = "#{@@purl_prefix}/BRO?conceptid=http%3A%2F%2Fbioontology.org%2Fontologies%2FActivity.owl%23Activity" + assert_equal expected_purl, cls.purl res = fetch_response(cls.purl) - assert_equal 200, res.status - assert_equal 'https://bioportal.bioontology.org/ontologies/BRO'\ - '?p=classes&conceptid=http%3A%2F%2Fbioontology.org%2Fontologies%2FActivity.owl%23Activity', - res.env[:url].to_s + assert_equal 302, res.status + assert_equal 'https://bioportal.bioontology.org/ontologies/BRO/classes?conceptid=http%3A%2F%2Fbioontology.org%2Fontologies%2FActivity.owl%23Activity', + res.headers['location'] end # Test PURL generation for a class in a UMLS format ontology @@ -40,10 +42,13 @@ def test_purl_umls ) refute_nil cls + # The ID already contains the PURL host, so .purl should return it as-is + assert_equal cls.id, cls.purl + res = fetch_response(cls.purl) - assert_equal 200, res.status - assert_equal 'https://bioportal.bioontology.org/ontologies/SNOMEDCT?p=classes&conceptid=64572001', - res.env[:url].to_s + assert_equal 302, res.status + assert_equal 'https://bioportal.bioontology.org/ontologies/SNOMEDCT/classes/64572001', + res.headers['location'] end # Test PURL generation for a class in an OBO format ontology @@ -54,20 +59,20 @@ def test_purl_obo ) refute_nil cls + expected_purl = "#{@@purl_prefix}/DOID?conceptid=http%3A%2F%2Fpurl.obolibrary.org%2Fobo%2FDOID_4" + assert_equal expected_purl, cls.purl + res = fetch_response(cls.purl) - assert_equal 200, res.status - assert_equal 'https://bioportal.bioontology.org/ontologies/DOID'\ - '?p=classes&conceptid=http%3A%2F%2Fpurl.obolibrary.org%2Fobo%2FDOID_4', - res.env[:url].to_s + assert_equal 302, res.status + assert_equal 'https://bioportal.bioontology.org/ontologies/DOID/classes?conceptid=http%3A%2F%2Fpurl.obolibrary.org%2Fobo%2FDOID_4', + res.headers['location'] end private def fetch_response(url) - conn = Faraday.new do |f| - f.response :follow_redirects + Faraday.new do |f| f.adapter Faraday.default_adapter - end - conn.get(url) + end.get(url) end end