diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 0650cf0..3a1750d 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -9,19 +9,22 @@ name: Ruby on: push: + branches: + - '**' # run on all branches + tags-ignore: + - '**' # skip all tags pull_request_target: - types: [opened, reopened] + types: [opened, reopened, synchronize] jobs: test: - runs-on: ubuntu-latest strategy: fail-fast: false matrix: - ruby-version: ['3.1'] + ruby-version: ['3.2'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 - name: Set up Ruby # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, # change this to (see https://github.com/ruby/setup-ruby#versioning): @@ -30,8 +33,14 @@ jobs: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: set up config file - run: cp config/config.test.rb config/config.rb + run: cp config/config.test.rb config/config.rb - name: Run tests env: UT_APIKEY: ${{ secrets.UT_APIKEY }} run: bundle exec rake test TESTOPTS="-v" + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: ${{ matrix.ruby-version }} + verbose: true diff --git a/.gitignore b/.gitignore index 9c6de78..446fd6d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,12 @@ *.gem .DS_Store -config/config.rb +/config/config.rb # Specific to IntelliJ .idea/ # Specific to rbenv -.ruby-version \ No newline at end of file +.ruby-version + +# Codecov local cache +/coverage/ diff --git a/Gemfile b/Gemfile index cd1e8ba..83b4947 100644 --- a/Gemfile +++ b/Gemfile @@ -6,4 +6,6 @@ gemspec gem 'pry' gem 'rake' -gem 'rubocop', '~> 1.43' +gem 'rubocop', '~> 1.81' +gem 'simplecov', require: false +gem 'simplecov-cobertura', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 94cafd0..c165d83 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,8 +1,8 @@ PATH remote: . specs: - ontologies_api_client (2.5.3) - activesupport (= 7.2.2.1) + ontologies_api_client (2.8.0) + activesupport (= 8.0.3) addressable (~> 2.8) excon faraday @@ -15,7 +15,7 @@ PATH GEM remote: https://rubygems.org/ specs: - activesupport (7.2.2.1) + activesupport (8.0.3) base64 benchmark (>= 0.3) bigdecimal @@ -27,96 +27,112 @@ GEM minitest (>= 5.1) securerandom (>= 0.3) tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) - ast (2.4.2) - base64 (0.2.0) - benchmark (0.4.0) - bigdecimal (3.1.9) + ast (2.4.3) + base64 (0.3.0) + benchmark (0.5.0) + bigdecimal (3.3.1) coderay (1.1.3) concurrent-ruby (1.3.5) - connection_pool (2.5.0) - drb (2.2.1) - excon (1.2.3) - faraday (2.12.2) + connection_pool (2.5.4) + docile (1.4.1) + drb (2.2.3) + excon (1.3.1) + logger + faraday (2.14.0) faraday-net_http (>= 2.0, < 3.5) json logger - faraday-excon (2.3.0) + faraday-excon (2.4.0) excon (>= 1.0.0) faraday (>= 2.11.0, < 3) - faraday-follow_redirects (0.3.0) + faraday-follow_redirects (0.4.0) faraday (>= 1, < 3) - faraday-multipart (1.1.0) + faraday-multipart (1.1.1) multipart-post (~> 2.0) - faraday-net_http (3.4.0) - net-http (>= 0.5.0) + faraday-net_http (3.4.2) + net-http (~> 0.5) i18n (1.14.7) concurrent-ruby (~> 1.0) - json (2.9.1) - language_server-protocol (3.17.0.4) - logger (1.6.5) + json (2.16.0) + language_server-protocol (3.17.0.5) + lint_roller (1.1.0) + logger (1.7.0) lz4-ruby (0.3.3) method_source (1.1.0) - minitest (5.25.4) + minitest (5.26.2) minitest-hooks (1.5.2) minitest (> 5.3) - multi_json (1.15.0) + multi_json (1.17.0) multipart-post (2.4.1) - net-http (0.6.0) - uri - oj (3.16.9) + net-http (0.8.0) + uri (>= 0.11.1) + oj (3.16.12) bigdecimal (>= 3.0) ostruct (>= 0.2) - ostruct (0.6.1) - parallel (1.26.3) - parser (3.3.7.1) + ostruct (0.6.3) + parallel (1.27.0) + parser (3.3.10.0) ast (~> 2.4.1) racc + prism (1.6.0) pry (0.15.2) coderay (~> 1.1) method_source (~> 1.0) - public_suffix (6.0.1) + public_suffix (6.0.2) racc (1.8.1) rainbow (3.1.1) - rake (13.2.1) - regexp_parser (2.10.0) - rubocop (1.71.2) + rake (13.3.1) + regexp_parser (2.11.3) + rexml (3.4.4) + rubocop (1.81.7) json (~> 2.3) - language_server-protocol (>= 3.17.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) - rubocop-ast (>= 1.38.0, < 2.0) + rubocop-ast (>= 1.47.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.38.0) - parser (>= 3.3.1.0) + rubocop-ast (1.48.0) + parser (>= 3.3.7.2) + prism (~> 1.4) ruby-progressbar (1.13.0) securerandom (0.4.1) + simplecov (0.22.0) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-cobertura (3.1.0) + rexml + simplecov (~> 0.19) + simplecov-html (0.13.2) + simplecov_json_formatter (0.1.4) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (3.1.4) - unicode-emoji (~> 4.0, >= 4.0.4) - unicode-emoji (4.0.4) - uri (1.0.3) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.1.0) + uri (1.1.1) PLATFORMS arm64-darwin-24 ruby - x86_64-darwin-16 - x86_64-darwin-17 - x86_64-linux DEPENDENCIES faraday-follow_redirects (~> 0.3) - minitest (~> 5.25) + minitest (~> 5.26) minitest-hooks (~> 1.5) ontologies_api_client! pry rake - rubocop (~> 1.43) + rubocop (~> 1.81) + simplecov + simplecov-cobertura BUNDLED WITH - 2.5.11 + 2.7.2 diff --git a/LICENSE.txt b/LICENSE.txt index 8b0d9e2..7edfb51 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2025, The Board of Trustees of Leland Stanford Junior University +Copyright (c) 2026, The Board of Trustees of Leland Stanford Junior University All rights reserved. Redistribution and use in source and binary forms, with or without modification, are diff --git a/README.md b/README.md index 4feda27..bb9a762 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,10 @@ Resources that should have save, update, and delete methods will need to include For questions please email [support@bioontology.org](support@bioontology.org.) -## License +## Contributors + + + -This project is licensed under the [FreeBSD License](LICENSE.txt) © 2025 The Board of Trustees of Leland Stanford Junior University. \ No newline at end of file +## License +The 2-Clause BSD License. See [LICENSE.txt](LICENSE.txt) for more information. diff --git a/lib/ontologies_api_client/analytics.rb b/lib/ontologies_api_client/analytics.rb index cbb85ac..93c2f04 100644 --- a/lib/ontologies_api_client/analytics.rb +++ b/lib/ontologies_api_client/analytics.rb @@ -1,6 +1,7 @@ module LinkedData::Client class Analytics HTTP = LinkedData::Client::HTTP + LOGGER = Logger.new($stdout) attr_accessor :onts, :date @@ -18,7 +19,11 @@ def self.last_month analytics.delete(:context) onts = [] analytics.keys.each do |ont| - views = analytics[ont][:"#{year_num}"][:"#{month_num}"] + views = analytics.dig(ont, :"#{year_num}", :"#{month_num}") + if views.nil? + LOGGER.debug("Analytics data missing for ontology: #{ont}, year: #{year_num}, month: #{month_num}") + views = 0 + end onts << {ont: ont, views: views} end data.onts = onts diff --git a/lib/ontologies_api_client/http.rb b/lib/ontologies_api_client/http.rb index 7e1622c..6ec4d73 100644 --- a/lib/ontologies_api_client/http.rb +++ b/lib/ontologies_api_client/http.rb @@ -69,7 +69,7 @@ def self.get(path, params = {}, options = {}) req.url path req.params = params.dup req.options[:timeout] = 60 - req.headers.merge(headers) + req.headers.merge!(headers) req.headers[:invalidate_cache] = invalidate_cache end rescue StandardError => e @@ -144,18 +144,50 @@ def self.patch(path, obj) response end - def self.delete(id) - puts "Deleting #{id}" if $DEBUG_API_CLIENT - response = conn.delete id - raise StandardError, response.body if response.status >= 500 - - response - end - def self.object_from_json(json) recursive_struct(load_json(json)) end + def self.delete(path, params = {}, options = {}) + headers = options[:headers] || {} + raw = options[:raw] || false # return response.body (string) + parse = options[:parse] || false # return parsed object (similar to 'get') + + params = (params || {}).dup + params = params.delete_if { |k, v| v.nil? || v.to_s.empty? } + + begin + puts "Deleting #{path} with #{params}" if $DEBUG_API_CLIENT + response = conn.delete do |req| + req.url path + req.params = params.dup + req.options[:timeout] = options[:timeout] || 60 + req.headers.merge!(headers) + end + rescue StandardError => e + query = Faraday::Utils.build_query(params) + pretty = path.dup + pretty += '?' unless query.empty? || pretty.include?('?') + raise e, "Problem deleting:\n#{pretty}#{query}\n\nError: #{e.message}\n#{e.backtrace.join("\n\t")}" + end + + raise StandardError, response.body if response.status >= 500 + + if raw + response.body + elsif parse + if response.respond_to?(:parsed_body) && response.parsed_body + obj = response.parsed_body + obj = obj.dup if obj.frozen? + else + obj = recursive_struct(load_json(response.body)) + end + obj + else + response # backward compatibility preserved + end + end + private def self.custom_req(obj, file, file_attribute, req) diff --git a/lib/ontologies_api_client/version.rb b/lib/ontologies_api_client/version.rb index c1f37f1..b4ad266 100644 --- a/lib/ontologies_api_client/version.rb +++ b/lib/ontologies_api_client/version.rb @@ -2,6 +2,6 @@ module LinkedData module Client - VERSION = '2.5.3' + VERSION = '2.8.0' end end diff --git a/ontologies_api_client.gemspec b/ontologies_api_client.gemspec index a95da6c..0bb5ebf 100644 --- a/ontologies_api_client.gemspec +++ b/ontologies_api_client.gemspec @@ -18,7 +18,7 @@ Gem::Specification.new do |gem| gem.require_paths = ['lib'] gem.version = LinkedData::Client::VERSION - gem.add_dependency('activesupport', '7.2.2.1') + gem.add_dependency('activesupport', '8.0.3') gem.add_dependency('addressable', '~> 2.8') gem.add_dependency('excon') gem.add_dependency('faraday') @@ -29,6 +29,6 @@ Gem::Specification.new do |gem| gem.add_dependency('oj') gem.add_development_dependency('faraday-follow_redirects', '~> 0.3') - gem.add_development_dependency('minitest', '~> 5.25') + gem.add_development_dependency('minitest', '~> 5.26') gem.add_development_dependency('minitest-hooks', '~> 1.5') end diff --git a/test/models/test_class.rb b/test/models/test_class.rb index c0d6942..5b597df 100644 --- a/test/models/test_class.rb +++ b/test/models/test_class.rb @@ -36,6 +36,8 @@ def test_purl_owl # Test PURL generation for a class in a UMLS format ontology def test_purl_umls + skip 'Disable until #41 is fixed: https://github.com/ncbo/ontologies_api_ruby_client/issues/41' + cls = LinkedData::Client::Models::Class.find( 'http://purl.bioontology.org/ontology/SNOMEDCT/64572001', 'https://bioportal.bioontology.org/ontologies/SNOMEDCT' @@ -53,6 +55,8 @@ def test_purl_umls # Test PURL generation for a class in an OBO format ontology def test_purl_obo + skip 'Disable until #41 is fixed: https://github.com/ncbo/ontologies_api_ruby_client/issues/41' + cls = LinkedData::Client::Models::Class.find( 'http://purl.obolibrary.org/obo/DOID_4', 'https://bioportal.bioontology.org/ontologies/DOID' @@ -64,8 +68,11 @@ def test_purl_obo 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'] + + expected_location = 'https://bioportal.bioontology.org/ontologies/DOID/classes?conceptid=http%3A%2F%2Fpurl.obolibrary.org%2Fobo%2FDOID_4' + actual_location = res.headers['location'] + normalize = ->(url) { url&.sub(%r{/(\?)}, '\1') } + assert_equal normalize.call(expected_location), normalize.call(actual_location) end private diff --git a/test/test_case.rb b/test/test_case.rb index 3e5a855..b4bc8a4 100644 --- a/test/test_case.rb +++ b/test/test_case.rb @@ -1,5 +1,22 @@ # frozen_string_literal: true +# Start simplecov if this is a coverage task or if it is run in the CI pipeline +if ENV['COVERAGE'] || ENV['CI'] + require 'simplecov' + require 'simplecov-cobertura' + SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.new([ + SimpleCov::Formatter::HTMLFormatter, + SimpleCov::Formatter::CoberturaFormatter, # writes coverage/cobertura.xml + ]) + + SimpleCov.start do + enable_coverage :branch + add_filter %w[/test/ /config/] + end +end + +require 'bundler/setup' +require 'pry' require 'minitest/autorun' require 'minitest/hooks/test' require_relative '../lib/ontologies_api_client'