From 1d53e3ab29a7fc74cb2d6742e3d8e34f8e7830ba Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Thu, 30 Oct 2025 16:50:20 -0400 Subject: [PATCH 01/79] test --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a2f5d030..5f16130ee 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Rails API application for managing DataCite providers, clients, prefixes and DOI ## Installation -Using Docker. +Using Docker. ```bash docker run -p 8065:80 datacite/lupo From 8efb13881fa8222e168257118da521a83e64ea7e Mon Sep 17 00:00:00 2001 From: codycooperross <50597551+codycooperross@users.noreply.github.com> Date: Wed, 21 May 2025 14:44:44 -0400 Subject: [PATCH 02/79] Implements JSON Schema for titles --- app/models/doi.rb | 19 +++++++++++++------ .../controlled_vocabularies/title_type.json | 13 +++++++++++++ app/models/schemas/doi/titles.json | 13 +++++++++++++ spec/fixtures/files/nasa_error.json | 6 ++++-- spec/requests/datacite_dois/post_spec.rb | 2 +- 5 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 app/models/schemas/doi/controlled_vocabularies/title_type.json create mode 100644 app/models/schemas/doi/titles.json diff --git a/app/models/doi.rb b/app/models/doi.rb index 6065c72c1..3bd159445 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -109,16 +109,22 @@ class Doi < ApplicationRecord validates_presence_of :doi validates_presence_of :url, if: Proc.new { |doi| doi.is_registered_or_findable? } - json_schema_validation = { - message: ->(errors) { errors }, - schema: PUBLISHER_JSON_SCHEMA - } - def validate_publisher_obj?(doi) doi.validatable? && doi.publisher_obj? && !(doi.publisher_obj.blank? || doi.publisher_obj.all?(nil)) end - validates :publisher_obj, if: ->(doi) { validate_publisher_obj?(doi) }, json: json_schema_validation + def validate_field?(field_name) + validatable? && self[field_name].present? + end + + def self.json_schema_validation(schema_name) + { + message: ->(errors) { errors }, + schema: Rails.root.join("app", "models", "schemas", "doi", "#{schema_name}.json") + } + end + + validates :publisher_obj, if: ->(doi) { validate_publisher_obj?(doi) }, json: json_schema_validation("publisher") # from https://www.crossref.org/blog/dois-and-matching-regular-expressions/ but using uppercase validates_format_of :doi, with: /\A10\.\d{4,5}\/[-._;()\/:a-zA-Z0-9*~$=]+\z/, on: :create @@ -131,6 +137,7 @@ def validate_publisher_obj?(doi) validate :check_dates, if: :dates? validate :check_rights_list, if: :rights_list? validate :check_titles, if: :titles? + validates :titles, if: proc { |doi| validate_field?(:titles) }, json: json_schema_validation("titles") validate :check_descriptions, if: :descriptions? validate :check_types, if: :types? validate :check_container, if: :container? diff --git a/app/models/schemas/doi/controlled_vocabularies/title_type.json b/app/models/schemas/doi/controlled_vocabularies/title_type.json new file mode 100644 index 000000000..bac077daa --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/title_type.json @@ -0,0 +1,13 @@ +{ + "type": ["string", "null"], + "$schema": "http://json-schema.org/draft-04/schema#", + "anyOf": [ + { + "type": "string", + "enum": ["AlternativeTitle", "Subtitle", "TranslatedTitle", "Other"] + }, + { + "type": "null" + } + ] +} diff --git a/app/models/schemas/doi/titles.json b/app/models/schemas/doi/titles.json new file mode 100644 index 000000000..453139159 --- /dev/null +++ b/app/models/schemas/doi/titles.json @@ -0,0 +1,13 @@ +{ + "type": "array", + "$schema": "http://json-schema.org/draft-04/schema#", + "items": { + "type": "object", + "properties": { + "title": { "type": "string" }, + "titleType": { "$ref": "controlled_vocabularies/title_type.json" }, + "lang": { "type": "string" } + }, + "additionalProperties": false + } +} diff --git a/spec/fixtures/files/nasa_error.json b/spec/fixtures/files/nasa_error.json index 82023f91c..2acff7e96 100644 --- a/spec/fixtures/files/nasa_error.json +++ b/spec/fixtures/files/nasa_error.json @@ -31,9 +31,11 @@ "familyName":"Ramasubramanian" } ], - "titles":{ + "titles": [ + { "title":"Tropical Cyclone Satellite Imagery and Wind Speed Dataset" - }, + } + ], "publisher": "Radiant MLHub", "publicationYear": 2020, "types":{ diff --git a/spec/requests/datacite_dois/post_spec.rb b/spec/requests/datacite_dois/post_spec.rb index 7e06e511f..03d0fdcea 100644 --- a/spec/requests/datacite_dois/post_spec.rb +++ b/spec/requests/datacite_dois/post_spec.rb @@ -1274,7 +1274,7 @@ end context "when the title changes" do - let(:titles) { { "title" => "Referee report. For: RESEARCH-3482 [version 5; referees: 1 approved, 1 approved with reservations]" } } + let(:titles) { [ { "title" => "Referee report. For: RESEARCH-3482 [version 5; referees: 1 approved, 1 approved with reservations]" } ] } let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } let(:valid_attributes) do { From 2ce744c6aa68b8e298b8cbce4310bf6d27caacd9 Mon Sep 17 00:00:00 2001 From: codycooperross <50597551+codycooperross@users.noreply.github.com> Date: Wed, 21 May 2025 15:23:15 -0400 Subject: [PATCH 03/79] Array.wrap titles attribute --- app/models/doi.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/doi.rb b/app/models/doi.rb index 3bd159445..27ab9fcae 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -1733,6 +1733,10 @@ def doi=(value) write_attribute(:doi, value.upcase) if value.present? end + def titles=(value) + write_attribute(:titles, Array.wrap(value)) + end + def formats=(value) write_attribute(:formats, Array.wrap(value)) end From 20e175bd02fb1d9b0d64afd484aee470e13fd78c Mon Sep 17 00:00:00 2001 From: codycooperross <50597551+codycooperross@users.noreply.github.com> Date: Wed, 21 May 2025 15:28:01 -0400 Subject: [PATCH 04/79] Linting --- app/models/doi.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/doi.rb b/app/models/doi.rb index 27ab9fcae..4c7346da6 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -1736,7 +1736,7 @@ def doi=(value) def titles=(value) write_attribute(:titles, Array.wrap(value)) end - + def formats=(value) write_attribute(:formats, Array.wrap(value)) end From 19213ec22601618af35300b3efe933bceb7cf75d Mon Sep 17 00:00:00 2001 From: codycooperross <50597551+codycooperross@users.noreply.github.com> Date: Thu, 22 May 2025 17:03:56 -0400 Subject: [PATCH 05/79] Addressed issue where a string submitted in the titles attribute would validate --- app/models/doi.rb | 19 +++---- .../error.yml | 53 +++++++------------ spec/requests/datacite_dois/patch_spec.rb | 2 +- 3 files changed, 27 insertions(+), 47 deletions(-) diff --git a/app/models/doi.rb b/app/models/doi.rb index 4c7346da6..a0caa4263 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -113,18 +113,15 @@ def validate_publisher_obj?(doi) doi.validatable? && doi.publisher_obj? && !(doi.publisher_obj.blank? || doi.publisher_obj.all?(nil)) end - def validate_field?(field_name) - validatable? && self[field_name].present? + def validate_json_attribute?(attribute) + validatable? && !self[attribute].nil? end - def self.json_schema_validation(schema_name) - { - message: ->(errors) { errors }, - schema: Rails.root.join("app", "models", "schemas", "doi", "#{schema_name}.json") - } + def schema_file_path(schema_name) + Rails.root.join("app", "models", "schemas", "doi", "#{schema_name}.json") end - validates :publisher_obj, if: ->(doi) { validate_publisher_obj?(doi) }, json: json_schema_validation("publisher") + validates :publisher_obj, if: ->(doi) { validate_publisher_obj?(doi) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("publisher") } } # from https://www.crossref.org/blog/dois-and-matching-regular-expressions/ but using uppercase validates_format_of :doi, with: /\A10\.\d{4,5}\/[-._;()\/:a-zA-Z0-9*~$=]+\z/, on: :create @@ -137,7 +134,7 @@ def self.json_schema_validation(schema_name) validate :check_dates, if: :dates? validate :check_rights_list, if: :rights_list? validate :check_titles, if: :titles? - validates :titles, if: proc { |doi| validate_field?(:titles) }, json: json_schema_validation("titles") + validates :titles, if: proc { |doi| doi.validate_json_attribute?(:titles) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("titles") } } validate :check_descriptions, if: :descriptions? validate :check_types, if: :types? validate :check_container, if: :container? @@ -1733,10 +1730,6 @@ def doi=(value) write_attribute(:doi, value.upcase) if value.present? end - def titles=(value) - write_attribute(:titles, Array.wrap(value)) - end - def formats=(value) write_attribute(:formats, Array.wrap(value)) end diff --git a/spec/fixtures/vcr_cassettes/DataciteDoisController/PATCH_/dois/_id/when_the_title_is_changed_wrong_format/error.yml b/spec/fixtures/vcr_cassettes/DataciteDoisController/PATCH_/dois/_id/when_the_title_is_changed_wrong_format/error.yml index 34e50a0c8..4ee491148 100644 --- a/spec/fixtures/vcr_cassettes/DataciteDoisController/PATCH_/dois/_id/when_the_title_is_changed_wrong_format/error.yml +++ b/spec/fixtures/vcr_cassettes/DataciteDoisController/PATCH_/dois/_id/when_the_title_is_changed_wrong_format/error.yml @@ -1,51 +1,38 @@ --- http_interactions: - request: - method: get - uri: https://api.datacite.org/re3data/10.17616/r3xs37 + method: put + uri: https://handle.test.datacite.org/api/handles/10.14454/4K3M-NYVG body: - encoding: US-ASCII - string: '' + encoding: UTF-8 + string: '[{"index":100,"type":"HS_ADMIN","data":{"format":"admin","value":{"handle":"","index":300,"permissions":"111111111111"}}},{"index":1,"type":"URL","data":{"format":"string","value":"http://www.bl.uk/pdf/pat.pdf"}}]' headers: User-Agent: - - Mozilla/5.0 (compatible; Maremma/4.9.6; mailto:info@datacite.org) + - Mozilla/5.0 (compatible; Maremma/5.0.0; mailto:info@datacite.org) Accept: - text/html,application/json,application/xml;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5 + Content-Type: + - application/json;charset=UTF-8 + Authorization: + - Basic Accept-Encoding: - gzip,deflate response: status: - code: 200 - message: OK + code: 401 + message: Unauthorized headers: Date: - - Wed, 04 May 2022 17:40:51 GMT - Content-Type: - - application/json; charset=utf-8 + - Thu, 22 May 2025 13:11:35 GMT + Content-Length: + - '0' Connection: - keep-alive - Status: - - 200 OK - Cache-Control: - - max-age=0, private, must-revalidate - Vary: - - Origin - Etag: - - W/"4779b1c8cb81d39142cef21ab1d60307" - X-Runtime: - - '0.261111' - X-Request-Id: - - eda1eb1c-86ed-44bc-abb8-e241e0c9da76 - X-Powered-By: - - Phusion Passenger 5.2.3 - Server: - - nginx/1.12.2 + Phusion Passenger 5.2.3 - Content-Encoding: - - gzip + Www-Authenticate: + - Basic realm="handle", Handle sessionId="node0jpq58pljxzd91vdly5e1oc72o5", + nonce="XJEPmnoCbjbSEdGkeuQcGQ==", error="Identity not verified" body: encoding: ASCII-8BIT - string: !binary |- - H4sIAAAAAAAAA6xY224bORL9FUJPDqDW1XHGflqN480IiGLDF3iR3XmguqslJmyyQbKt0QT+m/mMfZsf21PsltUdO8rMIoAfLLFYrMupw0N96WUyyN7Zl57Keme98WgwfnMyPhleT/91M33T6/fCtiQsOJpGw35PhuDUsgrkeVfz/Zw3u2k2Ho1G49F4eoydjkrrVbBu+0EW7ONq9uHd7GLWWbpzGivrEEp/NhxuNptBKc1KkhxkNOxYnlsTZBpw6r+/9AL9Fr69L61Nh73H/pOpMrn9x9537/HXfi8jnzpVBmXNPjyRiLcoibiqllr5NTmRWycupAtr8Z9qNJqciAvzoJw1BSEiLW5SRSYlL9bSC2mE1IX1QUxHyZakE2vluQaiXrQlmUSmMPdCq6WTWGH/0qVr9aDMqi/K+uD4vzSZyJT3VCgjA74SK7KOcnJ8JNY40tzZQoQ11UH2BbXD6yOkTCyVzdQDOfRjK3wT8EBcOrWCY623gh6s5vNrbzJ6XkpPMTpPmeJsRYrDfV+oIJQXyMXJgCg4NfHJKhjkMlWaD7F5DGmmcweLe1qRQSnnxgcVAJ6++IV0sbY6/C7O4dnVB11ZjZJxxAvplCFxTR5FTNfiaHY/fxVXOFPe0nSmMfxGT44Ws+u7BTaGGM6deaoCAvzZEbIaiAaYAuFknEqBAJDYvrD31mmERAHFt9quVIq+X7qVNOp3tMUacXS/uKzDQ2HQXyStnkpTb7+WmaqNF9bwXHC1m0SO7q8X568GYh7EBtXMK6AN+b3sKMKz2bjcxrzmXA5GiEU3xbmtDNoQW9egUxzNz29eCWXEBBMa68hwXRIZkZILKldo00YB5LG+6LO4dRVwfEPw6BXghq3j04G4Rf19FSGcVxqQqHHAVVhS2LDHXUG5kexuD2lEkMErz4O2yP/pRCAL0CqtyRhngdK1iVVWRam5S6EuHRm51Jg19po6632ymwauJnoawc3ppPWpadzHg1lPi6eAD3HaauT6qmxOgFfLjgH6GHCzdY8PtJbPdZTaAiFlcBAHcM90HIGpCnK28ki13ZRWYJ9shWZpP+gQ3HsQXyVXzKt7hiOzilTV9AghddfP0ajYJ25TtGTC7rgAf6Sq1Jhzt40WMgM2I1KYmDu2EVovMR+X78URQwq6CRwcugvXV8tP9BVZT8R7ldMTXWKjT9doLba9/ee7NldPxuJnxXO2PWA0FR9kqFyLgA8Zj6diFsDLJcYK0NhNBad1mZI0duVkuT543ng6mvx142PxjmIKEXlXUkvCrfTdpMbHmM7/b+dr3occvEpruOMjbrjDOb2uj2u2HazgCfvnjuFCc9u+WICfUX50KaZ47rYe1yE+f7+SJ/WpP8RZLHQcLr6Dj0BUusqYDBBtHcqrZ2nh7meNAFa5/XpabjCboLoUpWtvK6Xz1MboTQB2pMsw8eAaEplNMfcG+qSF62e75oXkAT9kcqUlWDoKnENmsx2FMQMddDhDOeyD8hVG5RvGqEfpYJORe1YQ3nLVLEb6+EzbjXVZhzdw5a/r4eoUqZR6j/qWGJO7UexYg1bjzLcMoVviyHbswDcZbiAHrdH5PnXbF6zzKOIUqt7yK1dggUqDPzoeIDVwATZgatt3JNVLBgi0jh2lVI3IAcXWRTK1/v2WFoLi/HtiCO1ml7sLo+FdxnQFJbVlQr24g1Gj3Y01Cdqbq4DvqmeCW24Ui20ytd7mO9irZVRxALkLuBPA0abSmjV9e/nCZO3FA/cKxBvObl0UGVXt+talSRqZmOxk4r4uyUfMlqsKkf/5XyeiTExEBRwsCFrYQ0LjMqlwWT4/BR3phv0M4lGb8vW5fy48CZAIeQyGiRLJdVB/fXl9NppO6GR5ejyJhs3Lo2PVftw0ta5oSS6pjB/aKCJ9FCnDQqHeS4JwdMOMglTa84dh7qT5nFjN2jVZaUvpZ1DvYB0KvvI56gZhL8nb/k5j8jPjoFhG7X4grAqJdjXAguij3+p4v27GD4PYU+5//hEaZd8XHdQ0yd8VG9Jhw68qA9DIHPfAC7DZYyE+IV6w+BvAYlY4gKHJyoylez39SxhqFxbEnA+St26QfAcjCICJfBZl+07z1U97fpIiO8wIHvYpC0OG76/xgYwtd6W2csf2ux8DalPKnu97enA7WrFQiMhmlDYR1O7e48o0vomjAS+oRIqbagl94REEXD/nqv0DfgiJiXnBGx+OS5XdQH1Q0Rm8t5fzuCpLVX//lT/f/q3BSjXc3YE4uqHOy9k8uVr8Et14m4eNxIXB8ryJ2eI94OrVPVX2xqen/AMIXghtesSbkF/LYGe8oybJ6E0yPrkdnZ5NT8/Grz9yuiVa1BhMYHCMv9vRT2fHP51Npx97j4+P/wMAAP//AwCTHMqHrhEAAA== - http_version: null - recorded_at: Wed, 04 May 2022 17:40:49 GMT -recorded_with: VCR 5.1.0 + string: '' + recorded_at: Thu, 22 May 2025 13:11:35 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/requests/datacite_dois/patch_spec.rb b/spec/requests/datacite_dois/patch_spec.rb index 01cba929d..608b10fd0 100644 --- a/spec/requests/datacite_dois/patch_spec.rb +++ b/spec/requests/datacite_dois/patch_spec.rb @@ -417,7 +417,7 @@ patch "/dois/#{doi.doi}", valid_attributes, headers expect(last_response.status).to eq(422) - expect(json["errors"]).to eq([{ "source" => "titles", "title" => "Title 'Submitted chemical data for InChIKey=YAPQBXQYLJRXSA-UHFFFAOYSA-N' should be an object instead of a string.", "uid" => "10.14454/4k3m-nyvg" }]) + expect(json["errors"]).to eq([{ "source" => "titles", "title" => "Root is not of type: array", "uid" => "10.14454/4k3m-nyvg" }]) end end From 1360a4c0e35abbd251ca143bf7cf16b71e44c8a3 Mon Sep 17 00:00:00 2001 From: codycooperross <50597551+codycooperross@users.noreply.github.com> Date: Thu, 22 May 2025 17:27:45 -0400 Subject: [PATCH 06/79] Fix title data structure in test --- spec/concerns/indexable_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/concerns/indexable_spec.rb b/spec/concerns/indexable_spec.rb index e73977977..86acb80d7 100644 --- a/spec/concerns/indexable_spec.rb +++ b/spec/concerns/indexable_spec.rb @@ -69,7 +69,7 @@ let!(:doi) do create( :doi, - titles: { title: "Soil investigations" }, + titles: { [ title: "Soil investigations" ] }, publisher: "Pangaea", descriptions: { description: "this is a description" }, aasm_state: "findable", From 04497cd78e022f316c1d836014241025b13cdbb2 Mon Sep 17 00:00:00 2001 From: codycooperross <50597551+codycooperross@users.noreply.github.com> Date: Fri, 23 May 2025 08:57:05 -0400 Subject: [PATCH 07/79] Fix data structure --- spec/concerns/indexable_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/concerns/indexable_spec.rb b/spec/concerns/indexable_spec.rb index 86acb80d7..3c28edecf 100644 --- a/spec/concerns/indexable_spec.rb +++ b/spec/concerns/indexable_spec.rb @@ -69,7 +69,7 @@ let!(:doi) do create( :doi, - titles: { [ title: "Soil investigations" ] }, + titles: [ { title: "Soil investigations" } ], publisher: "Pangaea", descriptions: { description: "this is a description" }, aasm_state: "findable", From 853dd156ef449a7b03f979fbd904ab4edd97b637 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Thu, 30 Oct 2025 17:32:51 -0400 Subject: [PATCH 08/79] test --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f16130ee..7a2f5d030 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Rails API application for managing DataCite providers, clients, prefixes and DOI ## Installation -Using Docker. +Using Docker. ```bash docker run -p 8065:80 datacite/lupo From 77e6295b651038e0d5b0bc60daa5ddcdbbe812e3 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Fri, 31 Oct 2025 12:43:56 -0400 Subject: [PATCH 09/79] Update activerecord_json_validator to the latest version. --- Gemfile | 2 +- Gemfile.lock | 16 ++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Gemfile b/Gemfile index 79a8d086e..23aeaf75d 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ source "https://rubygems.org" gem "aasm", "~> 5.0", ">= 5.0.1" gem "active_model_serializers", "~> 0.10.0" -gem "activerecord_json_validator", "~> 2.1", ">= 2.1.5" +gem 'activerecord_json_validator', '~> 3.1' gem "apollo-federation", "1.1.3" gem "audited", "~> 5.4", ">= 5.4.3" gem "aws-sdk-s3" diff --git a/Gemfile.lock b/Gemfile.lock index b38989a0d..c441aab75 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -66,9 +66,9 @@ GEM activemodel (= 7.1.3.2) activesupport (= 7.1.3.2) timeout (>= 0.4.0) - activerecord_json_validator (2.1.5) - activerecord (>= 4.2.0, < 8) - json_schemer (~> 0.2.18) + activerecord_json_validator (3.1.0) + activerecord (>= 4.2.0, < 9) + json_schemer (~> 2.2) activestorage (7.1.3.2) actionpack (= 7.1.3.2) activejob (= 7.1.3.2) @@ -278,8 +278,6 @@ GEM scanf (~> 1.0) sxp (~> 1.2) unicode-types (~> 1.7) - ecma-re-validator (0.4.0) - regexp_parser (~> 2.2) edtf (3.2.0) activesupport (>= 3.0, < 9.0) elasticsearch (7.17.10) @@ -390,12 +388,11 @@ GEM json-ld-preloaded (3.2.2) json-ld (~> 3.2) rdf (~> 3.2) - json_schemer (0.2.25) - ecma-re-validator (~> 0.3) + json_schemer (2.4.0) + bigdecimal hana (~> 1.3) regexp_parser (~> 2.0) simpleidn (~> 0.2) - uri_template (~> 0.7) jsonapi-renderer (0.2.2) jsonapi-serializer (2.2.0) activesupport (>= 4.2) @@ -771,7 +768,6 @@ GEM unicode_utils (1.4.0) uniform_notifier (1.16.0) uri (0.13.2) - uri_template (0.7.0) uuid (2.3.9) macaddr (~> 1.0) uuidtools (2.2.0) @@ -799,7 +795,7 @@ PLATFORMS DEPENDENCIES aasm (~> 5.0, >= 5.0.1) active_model_serializers (~> 0.10.0) - activerecord_json_validator (~> 2.1, >= 2.1.5) + activerecord_json_validator (~> 3.1) apollo-federation (= 1.1.3) audited (~> 5.4, >= 5.4.3) aws-sdk-s3 From 71143ae428486a5102c7d5d90736316cd0cb110b Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Fri, 31 Oct 2025 12:48:01 -0400 Subject: [PATCH 10/79] Appease rubocop. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 23aeaf75d..c8ebda2bb 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ source "https://rubygems.org" gem "aasm", "~> 5.0", ">= 5.0.1" gem "active_model_serializers", "~> 0.10.0" -gem 'activerecord_json_validator', '~> 3.1' +gem "activerecord_json_validator", "~> 3.1" gem "apollo-federation", "1.1.3" gem "audited", "~> 5.4", ">= 5.4.3" gem "aws-sdk-s3" From bdb63272c73ac133af2b4e1717a17d39cd7acd9d Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Fri, 7 Nov 2025 16:21:27 -0500 Subject: [PATCH 11/79] Update titles.json to use the latest schema and a couple more checks. --- app/models/schemas/doi/titles.json | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/models/schemas/doi/titles.json b/app/models/schemas/doi/titles.json index 453139159..c774d71ca 100644 --- a/app/models/schemas/doi/titles.json +++ b/app/models/schemas/doi/titles.json @@ -1,13 +1,21 @@ { "type": "array", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "items": { "type": "object", "properties": { - "title": { "type": "string" }, + "title": { + "type": "string", + "minLength": 1, + "pattern": "^(?!\\s*$).+" + }, "titleType": { "$ref": "controlled_vocabularies/title_type.json" }, - "lang": { "type": "string" } + "lang": { "type": ["string", "null"] } }, + "required": [ + "title" + ], "additionalProperties": false - } + }, + "minitems": 1 } From 42f9f59eb60f1852fd7bf7587dd1bdc54de6e34b Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Fri, 7 Nov 2025 16:42:40 -0500 Subject: [PATCH 12/79] Fix test --- spec/requests/datacite_dois/patch_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/requests/datacite_dois/patch_spec.rb b/spec/requests/datacite_dois/patch_spec.rb index 608b10fd0..64b881f5b 100644 --- a/spec/requests/datacite_dois/patch_spec.rb +++ b/spec/requests/datacite_dois/patch_spec.rb @@ -417,7 +417,7 @@ patch "/dois/#{doi.doi}", valid_attributes, headers expect(last_response.status).to eq(422) - expect(json["errors"]).to eq([{ "source" => "titles", "title" => "Root is not of type: array", "uid" => "10.14454/4k3m-nyvg" }]) + expect(json["errors"]).to eq([{ "source" => "titles", "title" => "Value at root is not an array", "uid" => "10.14454/4k3m-nyvg" }]) end end From b4b3451cf90b68911340aece375620d3e9397844 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 10 Nov 2025 12:19:15 -0500 Subject: [PATCH 13/79] fix titles schema --- app/models/schemas/doi/titles.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/models/schemas/doi/titles.json b/app/models/schemas/doi/titles.json index c774d71ca..eedc5938e 100644 --- a/app/models/schemas/doi/titles.json +++ b/app/models/schemas/doi/titles.json @@ -5,9 +5,7 @@ "type": "object", "properties": { "title": { - "type": "string", - "minLength": 1, - "pattern": "^(?!\\s*$).+" + "type": "string" }, "titleType": { "$ref": "controlled_vocabularies/title_type.json" }, "lang": { "type": ["string", "null"] } From 8a4a38bf460069c9b686cbda039390a19e98c5ed Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 10 Nov 2025 15:55:27 -0500 Subject: [PATCH 14/79] JSON schema for publicationYear. --- app/models/doi.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/doi.rb b/app/models/doi.rb index a0caa4263..c0d79b2ca 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -134,7 +134,6 @@ def schema_file_path(schema_name) validate :check_dates, if: :dates? validate :check_rights_list, if: :rights_list? validate :check_titles, if: :titles? - validates :titles, if: proc { |doi| doi.validate_json_attribute?(:titles) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("titles") } } validate :check_descriptions, if: :descriptions? validate :check_types, if: :types? validate :check_container, if: :container? @@ -149,6 +148,9 @@ def schema_file_path(schema_name) validate :check_geo_locations, if: :geo_locations? validate :check_language, if: :language? + validates :publication_year, if: proc { |doi| doi.validate_json_attribute?(:publication_year) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("publication_year") } } + validates :titles, if: proc { |doi| doi.validate_json_attribute?(:titles) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("titles") } } + after_commit :update_url, on: %i[create update] after_commit :update_media, on: %i[create update] From 49c5ff380c534d6d6bf38aa9be2ebf6079d34ae7 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 10 Nov 2025 17:03:28 -0500 Subject: [PATCH 15/79] JSON schema for publication_year. --- app/models/schemas/doi/publication_year.json | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 app/models/schemas/doi/publication_year.json diff --git a/app/models/schemas/doi/publication_year.json b/app/models/schemas/doi/publication_year.json new file mode 100644 index 000000000..9134a5406 --- /dev/null +++ b/app/models/schemas/doi/publication_year.json @@ -0,0 +1,21 @@ +{ + "type": [ "integer", "string" ], + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "publicationYear", + "oneOf": [ + { + "type": "string", + "pattern": "^(1[0-9]{3}|2[0-9]{3}|3000)$", + "minLength": 4, + "maxLength": 4 + }, + { + "type": "integer", + "minimum": 1000, + "maximum": 3000, + "inclusiveMinimum": true, + "inclusiveMaximum": true + } + ], + "description": "publicationYear: A required value representing a year in YYYY format." +} \ No newline at end of file From e90e0888d400bf151c7acd724949069c5c9ff4f2 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Tue, 11 Nov 2025 16:51:17 -0500 Subject: [PATCH 16/79] Add creators schema --- app/models/doi.rb | 6 +- .../controlled_vocabularies/name_type.json | 13 ++++ app/models/schemas/doi/creators.json | 71 +++++++++++++++++++ 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 app/models/schemas/doi/controlled_vocabularies/name_type.json create mode 100644 app/models/schemas/doi/creators.json diff --git a/app/models/doi.rb b/app/models/doi.rb index c0d79b2ca..78d657cc5 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -121,8 +121,6 @@ def schema_file_path(schema_name) Rails.root.join("app", "models", "schemas", "doi", "#{schema_name}.json") end - validates :publisher_obj, if: ->(doi) { validate_publisher_obj?(doi) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("publisher") } } - # from https://www.crossref.org/blog/dois-and-matching-regular-expressions/ but using uppercase validates_format_of :doi, with: /\A10\.\d{4,5}\/[-._;()\/:a-zA-Z0-9*~$=]+\z/, on: :create validates_format_of :url, with: /\A(ftp|http|https):\/\/\S+/, if: :url?, message: "URL is not valid" @@ -148,8 +146,12 @@ def schema_file_path(schema_name) validate :check_geo_locations, if: :geo_locations? validate :check_language, if: :language? + # json-schema validation validates :publication_year, if: proc { |doi| doi.validate_json_attribute?(:publication_year) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("publication_year") } } + validates :publisher_obj, if: ->(doi) { validate_publisher_obj?(doi) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("publisher") } } validates :titles, if: proc { |doi| doi.validate_json_attribute?(:titles) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("titles") } } + # validates :doi, if: proc { |doi| doi.validate_json_attribute?(:doi) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("identifier") } } + validates :creators, if: proc { |doi| doi.validate_json_attribute?(:creators) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("creators") } } after_commit :update_url, on: %i[create update] after_commit :update_media, on: %i[create update] diff --git a/app/models/schemas/doi/controlled_vocabularies/name_type.json b/app/models/schemas/doi/controlled_vocabularies/name_type.json new file mode 100644 index 000000000..f058cacdc --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/name_type.json @@ -0,0 +1,13 @@ +{ + "type": ["string", "null"], + "$schema": "https://json-schema.org/draft/2020-12/schema", + "anyOf": [ + { + "type": "string", + "enum": ["Organizational", "Personal"] + }, + { + "type": "null" + } + ] +} \ No newline at end of file diff --git a/app/models/schemas/doi/creators.json b/app/models/schemas/doi/creators.json new file mode 100644 index 000000000..85f732170 --- /dev/null +++ b/app/models/schemas/doi/creators.json @@ -0,0 +1,71 @@ +{ + "type": "array", + "title": "Creators", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "nameType": { + "$ref": "controlled_vocabularies/name_type.json" + }, + "givenName": { + "type": ["string", "null"] + }, + "familyName": { + "type": ["string", "null"] + }, + "affiliation": { + "type": "array", + "items": { + "type": "object", + "properties": { + "affiliationIdentifier": { + "type": ["string", "null"] + }, + "affiliationIdentifierScheme": { + "type": ["string", "null"] + }, + "name": { + "type": ["string", "null"] + }, + "schemeUri": { + "type": ["string", "null"] + } + }, + "dependentRequired": { + "affiliationIdentifier": ["affiliationIdentifierScheme"] + }, + "additionalProperties": false + } + }, + "nameIdentifiers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "schemeUri": { + "type": ["string", "null"] + }, + "nameIdentifier": { + "type": ["string", "null"] + }, + "nameIdentifierScheme": { + "type": ["string", "null"] + } + }, + "dependentRequired": { + "nameIdentifier": ["nameIdentifierScheme"] + } + } + } + }, + "additionalProperties": false, + "required": [ + "name" + ] + }, + "minItems": 1 +} \ No newline at end of file From 3337cc270dde23b513dc6a29b76c846470c5813a Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Tue, 11 Nov 2025 16:59:42 -0500 Subject: [PATCH 17/79] Update schema in title_type --- app/models/schemas/doi/controlled_vocabularies/title_type.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/schemas/doi/controlled_vocabularies/title_type.json b/app/models/schemas/doi/controlled_vocabularies/title_type.json index bac077daa..37aefbdcb 100644 --- a/app/models/schemas/doi/controlled_vocabularies/title_type.json +++ b/app/models/schemas/doi/controlled_vocabularies/title_type.json @@ -1,6 +1,6 @@ { "type": ["string", "null"], - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "anyOf": [ { "type": "string", From ac4864c207a16609e2fbef039379292d104d71ef Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Tue, 11 Nov 2025 22:37:23 -0500 Subject: [PATCH 18/79] Creators test should have failed. --- app/models/schemas/doi/creators.json | 1 + spec/requests/datacite_dois/post_spec.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/schemas/doi/creators.json b/app/models/schemas/doi/creators.json index 85f732170..e15f53b10 100644 --- a/app/models/schemas/doi/creators.json +++ b/app/models/schemas/doi/creators.json @@ -17,6 +17,7 @@ "familyName": { "type": ["string", "null"] }, + "lang": { "type": ["string", "null"] }, "affiliation": { "type": "array", "items": { diff --git a/spec/requests/datacite_dois/post_spec.rb b/spec/requests/datacite_dois/post_spec.rb index 03d0fdcea..3e4d1260a 100644 --- a/spec/requests/datacite_dois/post_spec.rb +++ b/spec/requests/datacite_dois/post_spec.rb @@ -893,7 +893,7 @@ it "fails to create a Doi" do post "/dois", valid_attributes, headers - expect(last_response.status).to eq(201) + expect(last_response.status).to eq(422) end end From 0c343ad75e67c623cfb7ff5ba3a3a6bbe56a8d07 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Wed, 12 Nov 2025 08:52:48 -0500 Subject: [PATCH 19/79] Temporarily comment out test. --- spec/requests/datacite_dois/post_spec.rb | 47 ++++++++++++------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/spec/requests/datacite_dois/post_spec.rb b/spec/requests/datacite_dois/post_spec.rb index 3e4d1260a..7a329bc36 100644 --- a/spec/requests/datacite_dois/post_spec.rb +++ b/spec/requests/datacite_dois/post_spec.rb @@ -1749,29 +1749,30 @@ end end - context "validates crosscite" do - let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("crosscite.json"))) } - let(:params) do - { - "data" => { - "type" => "dois", - "attributes" => { - "doi" => "10.14454/10703", - "xml" => xml, - }, - }, - } - end - - it "validates a Doi" do - post "/dois/validate", params, headers - - expect(last_response.status).to eq(200) - expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") - expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Analysis Tools for Crossover Experiment of UI using Choice Architecture" }]) - expect(json.dig("data", "attributes", "dates")).to eq([{ "date" => "2016-03-27", "dateType" => "Issued" }]) - end - end + # Temporarily removing this test + # context "validates crosscite" do + # let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("crosscite.json"))) } + # let(:params) do + # { + # "data" => { + # "type" => "dois", + # "attributes" => { + # "doi" => "10.14454/10703", + # "xml" => xml, + # }, + # }, + # } + # end + # + # it "validates a Doi" do + # post "/dois/validate", params, headers + + # expect(last_response.status).to eq(200) + # expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + # expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Analysis Tools for Crossover Experiment of UI using Choice Architecture" }]) + # expect(json.dig("data", "attributes", "dates")).to eq([{ "date" => "2016-03-27", "dateType" => "Issued" }]) + # end + # end context "validates bibtex" do let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("crossref.bib"))) } From c6d150f5f8effa78b8fb2e8a8d37efbd81049c81 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Wed, 12 Nov 2025 08:55:29 -0500 Subject: [PATCH 20/79] Temporarily comment out test. --- spec/requests/datacite_dois/post_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/requests/datacite_dois/post_spec.rb b/spec/requests/datacite_dois/post_spec.rb index 7a329bc36..28582ddfe 100644 --- a/spec/requests/datacite_dois/post_spec.rb +++ b/spec/requests/datacite_dois/post_spec.rb @@ -1763,10 +1763,10 @@ # }, # } # end - # + # # it "validates a Doi" do # post "/dois/validate", params, headers - + # # expect(last_response.status).to eq(200) # expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") # expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Analysis Tools for Crossover Experiment of UI using Choice Architecture" }]) From 19a710c7f15d6b445681d99675ded086cfc2edb5 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Wed, 12 Nov 2025 11:19:52 -0500 Subject: [PATCH 21/79] Fix test - creator missing name --- spec/requests/datacite_dois/post_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/requests/datacite_dois/post_spec.rb b/spec/requests/datacite_dois/post_spec.rb index 28582ddfe..7dcb37aa4 100644 --- a/spec/requests/datacite_dois/post_spec.rb +++ b/spec/requests/datacite_dois/post_spec.rb @@ -813,6 +813,7 @@ "nameType": "Personal", "givenName": "Julia M.", "familyName": "Rovera", + "name": "Rovera, Julia M.", "affiliation": [{ "name": "Drexel University" }], From da0e51d429743fb2c3fc6b168e102d84421cb53b Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Wed, 12 Nov 2025 11:41:59 -0500 Subject: [PATCH 22/79] Fix test. Creator.affiliation was not an array. --- spec/graphql/types/work_type_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/graphql/types/work_type_spec.rb b/spec/graphql/types/work_type_spec.rb index 51c2f9995..bb54af799 100644 --- a/spec/graphql/types/work_type_spec.rb +++ b/spec/graphql/types/work_type_spec.rb @@ -934,9 +934,9 @@ { "name" => "Kristian Garza", "nameType" => "Personal", - "affiliation" => { + "affiliation" => [{ "name" => "Ruhr-University Bochum, Germany" - } + }] }, { "familyName" => "Garza", From d3d118b8250212a021bd7823716eff51a0395923 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Thu, 13 Nov 2025 14:33:04 -0500 Subject: [PATCH 23/79] Don't validate json on /dois/validate enpoint. Fix a couple of tests. --- app/models/doi.rb | 11 +++--- spec/requests/datacite_dois/post_spec.rb | 48 ++++++++++++------------ 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/app/models/doi.rb b/app/models/doi.rb index 78d657cc5..7c69896d4 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -147,11 +147,12 @@ def schema_file_path(schema_name) validate :check_language, if: :language? # json-schema validation - validates :publication_year, if: proc { |doi| doi.validate_json_attribute?(:publication_year) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("publication_year") } } - validates :publisher_obj, if: ->(doi) { validate_publisher_obj?(doi) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("publisher") } } - validates :titles, if: proc { |doi| doi.validate_json_attribute?(:titles) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("titles") } } - # validates :doi, if: proc { |doi| doi.validate_json_attribute?(:doi) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("identifier") } } - validates :creators, if: proc { |doi| doi.validate_json_attribute?(:creators) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("creators") } } + validates :publication_year, if: proc { |doi| doi.validate_json_attribute?(:publication_year) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("publication_year") } }, unless: :only_validate + validates :publisher_obj, if: ->(doi) { validate_publisher_obj?(doi) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("publisher") } }, unless: :only_validate + validates :titles, if: proc { |doi| doi.validate_json_attribute?(:titles) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("titles") } }, unless: :only_validate + # validates :doi, if: proc { |doi| doi.validate_json_attribute?(:doi) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("identifier") } }, unless: :only_validate + validates :creators, if: proc { |doi| doi.validate_json_attribute?(:creators) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("creators") } }, unless: :only_validate + # validates :contributors, if: proc { |doi| doi.validate_json_attribute?(:contributors) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("contributors") } }, unless: :only_validate after_commit :update_url, on: %i[create update] after_commit :update_media, on: %i[create update] diff --git a/spec/requests/datacite_dois/post_spec.rb b/spec/requests/datacite_dois/post_spec.rb index 7dcb37aa4..b55f1f028 100644 --- a/spec/requests/datacite_dois/post_spec.rb +++ b/spec/requests/datacite_dois/post_spec.rb @@ -898,6 +898,7 @@ end end + # There were no nameIdentifiers in contributors/creators. Added them so that would be tested. context "when the request has wrong object in nameIdentifiers nasa" do let(:valid_attributes) { JSON.parse(file_fixture("nasa_error.json").read) } @@ -1750,30 +1751,29 @@ end end - # Temporarily removing this test - # context "validates crosscite" do - # let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("crosscite.json"))) } - # let(:params) do - # { - # "data" => { - # "type" => "dois", - # "attributes" => { - # "doi" => "10.14454/10703", - # "xml" => xml, - # }, - # }, - # } - # end - # - # it "validates a Doi" do - # post "/dois/validate", params, headers - # - # expect(last_response.status).to eq(200) - # expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") - # expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Analysis Tools for Crossover Experiment of UI using Choice Architecture" }]) - # expect(json.dig("data", "attributes", "dates")).to eq([{ "date" => "2016-03-27", "dateType" => "Issued" }]) - # end - # end + context "validates crosscite" do + let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("crosscite.json"))) } + let(:params) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "xml" => xml, + }, + }, + } + end + + it "validates a Doi" do + post "/dois/validate", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Analysis Tools for Crossover Experiment of UI using Choice Architecture" }]) + expect(json.dig("data", "attributes", "dates")).to eq([{ "date" => "2016-03-27", "dateType" => "Issued" }]) + end + end context "validates bibtex" do let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("crossref.bib"))) } From d9c046e746e89dae0098b3605860083ed21730c7 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Thu, 13 Nov 2025 14:40:45 -0500 Subject: [PATCH 24/79] Fix some test data - errors detected by validating with json schema. --- .../fixtures/files/datacite-user-example.json | 1 + spec/fixtures/files/nasa_error.json | 22 +++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/spec/fixtures/files/datacite-user-example.json b/spec/fixtures/files/datacite-user-example.json index 32bcaf699..7ae61d0bc 100644 --- a/spec/fixtures/files/datacite-user-example.json +++ b/spec/fixtures/files/datacite-user-example.json @@ -11,6 +11,7 @@ "nameType": "Personal", "givenName": "Julia M.", "familyName": "Rovera", + "name": "Rovera, Julia M.", "affiliation": [{ "name": "Drexel University" }], diff --git a/spec/fixtures/files/nasa_error.json b/spec/fixtures/files/nasa_error.json index 2acff7e96..c7c01b433 100644 --- a/spec/fixtures/files/nasa_error.json +++ b/spec/fixtures/files/nasa_error.json @@ -10,7 +10,14 @@ "name":"Maskey, Manil", "nameType":"Personal", "givenName":"Manil", - "familyName":"Maskey" + "familyName":"Maskey", + "nameIdentifiers": [ + { + "schemeUri": "https://orcid.org", + "nameIdentifier": "https://orcid.org/0000-0001-5727-2427", + "nameIdentifierScheme": "ORCID" + } + ] }, { "name":"Ramachandran, Rahul", @@ -42,11 +49,18 @@ "resourceTypeGeneral":"Dataset" }, "version":1.0, - "contributors":{ + "contributors": [{ "nameType":"Organizational", "name":"NASA Interagency Implementation and Advanced Concepts Team (IMPACT)", - "contributorType":"DataCurator" - }, + "contributorType":"DataCurator", + "nameIdentifiers": [ + { + "schemeUri": "https://orcid.org", + "nameIdentifier": "https://orcid.org/0000-0001-5727-2427", + "nameIdentifierScheme": "ORCID" + } + ] + }], "dates":{ "dateType":"Valid", "date": "2000/2019" From e5f7e96e15bffdf4a7e8edcfa8cd7107f1ea7ed1 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Thu, 13 Nov 2025 16:27:39 -0500 Subject: [PATCH 25/79] Fix test data --- spec/graphql/types/work_type_spec.rb | 29 +++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/spec/graphql/types/work_type_spec.rb b/spec/graphql/types/work_type_spec.rb index bb54af799..551d5af7a 100644 --- a/spec/graphql/types/work_type_spec.rb +++ b/spec/graphql/types/work_type_spec.rb @@ -1092,22 +1092,22 @@ "name" => "Kristian Garza", "nameType" => "Personal", "nameIdentifiers" => - { + [{ "schemeUri": "https://orcid.org", "nameIdentifier": "https://orcid.org/0000-0002-7105-9881", "nameIdentifierScheme": "ORCID" - } + }] }, { "name" => "Ross, Cody", "familyName" => "Ross", "givenName" => "Cody", "nameIdentifiers" => - { + [{ "nameIdentifier" => "https://orcid.org/0000-0003-3484-6875", "nameIdentifierScheme" => "ORCID", "schemeUri" => "https://orcid.org", - }, + }], "nameType" => "Personal", }, ], @@ -1117,22 +1117,22 @@ "familyName" => "Ross", "contributorType" => "Editor", "nameIdentifiers" => - { + [{ "schemeUri": "https://orcid.org", "nameIdentifier": "https://orcid.org/0000-0002-7105-9881", "nameIdentifierScheme": "ORCID" - } + }] }, { "givenName" => "Kristian", "familyName" => "Garza", "contributorType" => "Editor", "nameIdentifiers" => - { + [{ "nameIdentifier" => "https://orcid.org/0000-0003-3484-6875", "nameIdentifierScheme" => "ORCID", "schemeUri" => "https://orcid.org", - }, + }], }, ], ) @@ -1541,6 +1541,7 @@ create_list(:doi, 5, aasm_state: "findable", types: { "resourceTypeGeneral" => "Text" }, creators: [{ + name: "test_name-5", affiliation: [{ "name": "5", "affiliationIdentifier": "https://ror.org/5", @@ -1554,6 +1555,7 @@ create_list(:doi, 4, aasm_state: "findable", types: { "resourceTypeGeneral" => "JournalArticle" }, creators: [{ + name: "test_name-4", affiliation: [{ "name": "4", "affiliationIdentifier": "https://ror.org/4", @@ -1567,6 +1569,7 @@ create_list(:doi, 3, aasm_state: "findable", types: { "resourceTypeGeneral" => "Image" }, creators: [{ + name: "test_name-3", affiliation: [{ "name": "3", "affiliationIdentifier": "https://ror.org/3", @@ -1580,6 +1583,7 @@ create_list(:doi, 2, aasm_state: "findable", types: { "resourceTypeGeneral" => "PhysicalObject" }, creators: [{ + name: "test_name-2", affiliation: [{ "name": "2", "affiliationIdentifier": "https://ror.org/2", @@ -1593,6 +1597,7 @@ create(:doi, aasm_state: "findable", types: { "resourceTypeGeneral" => "Preprint" }, creators: [{ + name: "test_name-1", affiliation: [ { "name": "1", @@ -1613,7 +1618,9 @@ end let!(:missing) do create_list(:doi, 3, aasm_state: "findable", - creators: [{ affiliation: [] }], + creators: [{ + name: "test_name-3", + affiliation: [] }], rights_list: []) end @@ -1653,9 +1660,9 @@ { "name" => "Kristian Garza", "nameType" => "Personal", - "affiliation" => { + "affiliation" => [{ "name" => "Ruhr-University Bochum, Germany" - } + }] }, { "familyName" => "Garza", From 0351dc26a00762e515a0123500561d34724fe3c3 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Thu, 13 Nov 2025 16:38:06 -0500 Subject: [PATCH 26/79] Add contributors schema. --- app/models/schemas/doi/contributors.json | 81 +++++++++++++++++++ .../contributor_type.json | 36 +++++++++ 2 files changed, 117 insertions(+) create mode 100644 app/models/schemas/doi/contributors.json create mode 100644 app/models/schemas/doi/controlled_vocabularies/contributor_type.json diff --git a/app/models/schemas/doi/contributors.json b/app/models/schemas/doi/contributors.json new file mode 100644 index 000000000..dcc348333 --- /dev/null +++ b/app/models/schemas/doi/contributors.json @@ -0,0 +1,81 @@ +{ + "type": "array", + "title": "Contributors", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "nameType": { + "$ref": "controlled_vocabularies/name_type.json" + }, + "givenName": { + "type": ["string", "null"] + }, + "familyName": { + "type": ["string", "null"] + }, + "contributorType": { + "oneOf": [ + { + "$ref": "controlled_vocabularies/contributor_type.json" + }, + { + "type": "null" + } + ] + }, + "lang": { "type": ["string", "null"] }, + "affiliation": { + "type": "array", + "items": { + "type": "object", + "properties": { + "affiliationIdentifier": { + "type": ["string", "null"] + }, + "affiliationIdentifierScheme": { + "type": ["string", "null"] + }, + "name": { + "type": ["string", "null"] + }, + "schemeUri": { + "type": ["string", "null"] + } + }, + "dependentRequired": { + "affiliationIdentifier": ["affiliationIdentifierScheme"] + }, + "additionalProperties": false + } + }, + "nameIdentifiers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "schemeUri": { + "type": ["string", "null"] + }, + "nameIdentifier": { + "type": ["string", "null"] + }, + "nameIdentifierScheme": { + "type": ["string", "null"] + } + }, + "dependentRequired": { + "nameIdentifier": ["nameIdentifierScheme"] + } + } + } + }, + "additionalProperties": false, + "required": [ + "name" + ] + } +} \ No newline at end of file diff --git a/app/models/schemas/doi/controlled_vocabularies/contributor_type.json b/app/models/schemas/doi/controlled_vocabularies/contributor_type.json new file mode 100644 index 000000000..637f7b41e --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/contributor_type.json @@ -0,0 +1,36 @@ +{ + "type": ["string", "null"], + "$schema": "https://json-schema.org/draft/2020-12/schema", + "anyOf": [ + { + "type": "string", + "enum": [ + "ContactPerson", + "DataCollector", + "DataCurator", + "DataManager", + "Distributor", + "Editor", + "HostingInstitution", + "Producer", + "ProjectLeader", + "ProjectManager", + "ProjectMember", + "RegistrationAgency", + "RegistrationAuthority", + "RelatedPerson", + "Researcher", + "ResearchGroup", + "RightsHolder", + "Sponsor", + "Supervisor", + "Translator", + "WorkPackageLeader", + "Other" + ] + }, + { + "type": "null" + } + ] +} \ No newline at end of file From 9107c7d558e4439b112940482dc4c195511295e6 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Thu, 13 Nov 2025 17:29:28 -0500 Subject: [PATCH 27/79] Create shared schemas. --- app/models/schemas/doi/contributors.json | 42 +------------------ app/models/schemas/doi/creators.json | 42 +------------------ .../schemas/doi/shared/affiliations.json | 26 ++++++++++++ .../schemas/doi/shared/name_identifiers.json | 23 ++++++++++ 4 files changed, 53 insertions(+), 80 deletions(-) create mode 100644 app/models/schemas/doi/shared/affiliations.json create mode 100644 app/models/schemas/doi/shared/name_identifiers.json diff --git a/app/models/schemas/doi/contributors.json b/app/models/schemas/doi/contributors.json index dcc348333..5d06a29db 100644 --- a/app/models/schemas/doi/contributors.json +++ b/app/models/schemas/doi/contributors.json @@ -29,48 +29,10 @@ }, "lang": { "type": ["string", "null"] }, "affiliation": { - "type": "array", - "items": { - "type": "object", - "properties": { - "affiliationIdentifier": { - "type": ["string", "null"] - }, - "affiliationIdentifierScheme": { - "type": ["string", "null"] - }, - "name": { - "type": ["string", "null"] - }, - "schemeUri": { - "type": ["string", "null"] - } - }, - "dependentRequired": { - "affiliationIdentifier": ["affiliationIdentifierScheme"] - }, - "additionalProperties": false - } + "$ref": "shared/affiliations.json" }, "nameIdentifiers": { - "type": "array", - "items": { - "type": "object", - "properties": { - "schemeUri": { - "type": ["string", "null"] - }, - "nameIdentifier": { - "type": ["string", "null"] - }, - "nameIdentifierScheme": { - "type": ["string", "null"] - } - }, - "dependentRequired": { - "nameIdentifier": ["nameIdentifierScheme"] - } - } + "$ref": "shared/name_identifiers.json" } }, "additionalProperties": false, diff --git a/app/models/schemas/doi/creators.json b/app/models/schemas/doi/creators.json index e15f53b10..aa2dd9f5f 100644 --- a/app/models/schemas/doi/creators.json +++ b/app/models/schemas/doi/creators.json @@ -19,48 +19,10 @@ }, "lang": { "type": ["string", "null"] }, "affiliation": { - "type": "array", - "items": { - "type": "object", - "properties": { - "affiliationIdentifier": { - "type": ["string", "null"] - }, - "affiliationIdentifierScheme": { - "type": ["string", "null"] - }, - "name": { - "type": ["string", "null"] - }, - "schemeUri": { - "type": ["string", "null"] - } - }, - "dependentRequired": { - "affiliationIdentifier": ["affiliationIdentifierScheme"] - }, - "additionalProperties": false - } + "$ref": "shared/affiliations.json" }, "nameIdentifiers": { - "type": "array", - "items": { - "type": "object", - "properties": { - "schemeUri": { - "type": ["string", "null"] - }, - "nameIdentifier": { - "type": ["string", "null"] - }, - "nameIdentifierScheme": { - "type": ["string", "null"] - } - }, - "dependentRequired": { - "nameIdentifier": ["nameIdentifierScheme"] - } - } + "$ref": "shared/name_identifiers.json" } }, "additionalProperties": false, diff --git a/app/models/schemas/doi/shared/affiliations.json b/app/models/schemas/doi/shared/affiliations.json new file mode 100644 index 000000000..1f6d260bb --- /dev/null +++ b/app/models/schemas/doi/shared/affiliations.json @@ -0,0 +1,26 @@ +{ + "type": "array", + "title": "Affiliations", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "items": { + "type": "object", + "properties": { + "affiliationIdentifier": { + "type": ["string", "null"] + }, + "affiliationIdentifierScheme": { + "type": ["string", "null"] + }, + "name": { + "type": ["string", "null"] + }, + "schemeUri": { + "type": ["string", "null"] + } + }, + "dependentRequired": { + "affiliationIdentifier": ["affiliationIdentifierScheme"] + }, + "additionalProperties": false + } +} \ No newline at end of file diff --git a/app/models/schemas/doi/shared/name_identifiers.json b/app/models/schemas/doi/shared/name_identifiers.json new file mode 100644 index 000000000..1d8013c96 --- /dev/null +++ b/app/models/schemas/doi/shared/name_identifiers.json @@ -0,0 +1,23 @@ +{ + "type": "array", + "title": "NameIdentifiers", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "items": { + "type": "object", + "properties": { + "schemeUri": { + "type": ["string", "null"] + }, + "nameIdentifier": { + "type": ["string", "null"] + }, + "nameIdentifierScheme": { + "type": ["string", "null"] + } + }, + "dependentRequired": { + "affiliationIdentifier": ["nameIdentifierScheme"] + }, + "additionalProperties": false + } +} \ No newline at end of file From 2833a4df72e8bef5fb106bd2e542a8eb35f23ded Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Fri, 14 Nov 2025 14:11:37 -0500 Subject: [PATCH 28/79] Add subjects schema. --- app/models/doi.rb | 3 ++- app/models/schemas/doi/subjects.json | 35 ++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 app/models/schemas/doi/subjects.json diff --git a/app/models/doi.rb b/app/models/doi.rb index 7c69896d4..2c4375dbd 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -152,7 +152,8 @@ def schema_file_path(schema_name) validates :titles, if: proc { |doi| doi.validate_json_attribute?(:titles) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("titles") } }, unless: :only_validate # validates :doi, if: proc { |doi| doi.validate_json_attribute?(:doi) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("identifier") } }, unless: :only_validate validates :creators, if: proc { |doi| doi.validate_json_attribute?(:creators) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("creators") } }, unless: :only_validate - # validates :contributors, if: proc { |doi| doi.validate_json_attribute?(:contributors) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("contributors") } }, unless: :only_validate + validates :contributors, if: proc { |doi| doi.validate_json_attribute?(:contributors) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("contributors") } }, unless: :only_validate + validates :subjects, if: proc { |doi| doi.validate_json_attribute?(:subjects) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("subjects") } }, unless: :only_validate after_commit :update_url, on: %i[create update] after_commit :update_media, on: %i[create update] diff --git a/app/models/schemas/doi/subjects.json b/app/models/schemas/doi/subjects.json new file mode 100644 index 000000000..b7af6d634 --- /dev/null +++ b/app/models/schemas/doi/subjects.json @@ -0,0 +1,35 @@ +{ + "type": "array", + "title": "Subjects", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "minItems": 0, + "items": { + "type": "object", + "properties": { + "subject": { + "type": ["string", "null"] + }, + "schemeUri": { + "type": ["string", "null"], + "format": "uri" + }, + "valueUri": { + "type": ["string", "null"], + "format": "uri" + }, + "subjectScheme": { + "type": ["string", "null" ] + }, + "classificationCode": { + "type": ["string", "null" ] + }, + "lang": { + "type": ["string", "null" ] + } + }, + "required": [ + "subject" + ], + "additionalProperties": false + } +} \ No newline at end of file From e3b1429f0cf3d12fe78eaf922e0c4bbd55e44b06 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Fri, 14 Nov 2025 14:26:52 -0500 Subject: [PATCH 29/79] Fix test data --- spec/graphql/types/work_type_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/graphql/types/work_type_spec.rb b/spec/graphql/types/work_type_spec.rb index 551d5af7a..c11ff6e83 100644 --- a/spec/graphql/types/work_type_spec.rb +++ b/spec/graphql/types/work_type_spec.rb @@ -1113,6 +1113,7 @@ ], contributors: [ { + "name" => "Ross, Cody", "givenName" => "Cody", "familyName" => "Ross", "contributorType" => "Editor", @@ -1124,6 +1125,7 @@ }] }, { + "name" => "Garza, Kristian", "givenName" => "Kristian", "familyName" => "Garza", "contributorType" => "Editor", From de18655aa3a34c22712bd62b5b373253f0986165 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Fri, 14 Nov 2025 15:02:51 -0500 Subject: [PATCH 30/79] Fix test data. --- spec/graphql/types/work_type_spec.rb | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/spec/graphql/types/work_type_spec.rb b/spec/graphql/types/work_type_spec.rb index c11ff6e83..8009e49b6 100644 --- a/spec/graphql/types/work_type_spec.rb +++ b/spec/graphql/types/work_type_spec.rb @@ -967,14 +967,16 @@ { "givenName" => "Cody", "familyName" => "Ross", + "name" => "Ross, Cody", "contributorType" => "Editor", - "affiliation" => { + "affiliation" => [{ "name" => "Ruhr-University Bochum, Germany" - } + }] }, { "givenName" => "Kristian", "familyName" => "Garza", + "name" => "Garza, Kristian", "contributorType" => "Editor", "affiliation" => [ { @@ -1089,7 +1091,7 @@ aasm_state: "findable", creators: [ { - "name" => "Kristian Garza", + "name" => "Garza, Kristian", "nameType" => "Personal", "nameIdentifiers" => [{ @@ -1194,7 +1196,7 @@ expect(response.dig("data", "works", "nodes", 0, "creators")).to eq( [ { "id" => "https://orcid.org/0000-0002-7105-9881", - "name" => "Kristian Garza", + "name" => "Garza, Kristian", "givenName" => nil, "familyName" => nil }, { "id" => "https://orcid.org/0000-0003-3484-6875", @@ -1207,12 +1209,12 @@ expect(response.dig("data", "works", "nodes", 0, "contributors")).to eq( [ { "id" => "https://orcid.org/0000-0002-7105-9881", - "name" => nil, + "name" => "Ross, Cody", "givenName" => "Cody", "familyName" => "Ross", "contributorType" => "Editor" }, { "id" => "https://orcid.org/0000-0003-3484-6875", - "name" => nil, + "name" => "Garza, Kristian", "givenName" => "Kristian", "familyName" => "Garza", "contributorType" => "Editor" } @@ -1700,18 +1702,18 @@ }, ], "contributorType" => "Editor", - "affiliation" => { + "affiliation" => [{ "name" => "Ruhr-University Bochum, Germany", "affiliationIdentifier": "https://ror.org/013meh722", "affiliationIdentifierScheme": "ROR" - } + }] }, { "givenName" => "Kristian", "familyName" => "Garza", + "name" => "Garza, Kristian", "contributorType" => "Editor", - "affiliation" => [ - { + "affiliation" => [{ "name" => "University of Cambridge" } ] From 15c2f2f06ad9d35efc29739c944a6eee6b01ce88 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Fri, 14 Nov 2025 15:21:27 -0500 Subject: [PATCH 31/79] Fix test data. --- spec/graphql/types/work_type_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/graphql/types/work_type_spec.rb b/spec/graphql/types/work_type_spec.rb index 8009e49b6..7e5bad690 100644 --- a/spec/graphql/types/work_type_spec.rb +++ b/spec/graphql/types/work_type_spec.rb @@ -1183,7 +1183,7 @@ expect(response.dig("data", "works", "authors").length).to eq(2) expect(response.dig("data", "works", "authors", 0)).to eq( { "id" => "https://orcid.org/0000-0002-7105-9881", - "title" => "Kristian Garza", + "title" => "Garza, Kristian", "count" => 1 } ) end From 156e56f11ec8be12eaa83c645063f10017e33e3b Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 17 Nov 2025 16:32:42 -0500 Subject: [PATCH 32/79] Dates schema --- app/models/doi.rb | 1 + .../controlled_vocabularies/date_type.json | 26 ++++++ app/models/schemas/doi/dates.json | 87 +++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 app/models/schemas/doi/controlled_vocabularies/date_type.json create mode 100644 app/models/schemas/doi/dates.json diff --git a/app/models/doi.rb b/app/models/doi.rb index 2c4375dbd..e39288047 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -154,6 +154,7 @@ def schema_file_path(schema_name) validates :creators, if: proc { |doi| doi.validate_json_attribute?(:creators) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("creators") } }, unless: :only_validate validates :contributors, if: proc { |doi| doi.validate_json_attribute?(:contributors) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("contributors") } }, unless: :only_validate validates :subjects, if: proc { |doi| doi.validate_json_attribute?(:subjects) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("subjects") } }, unless: :only_validate + validates :dates, if: proc { |doi| doi.validate_json_attribute?(:dates) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("dates") } }, unless: :only_validate after_commit :update_url, on: %i[create update] after_commit :update_media, on: %i[create update] diff --git a/app/models/schemas/doi/controlled_vocabularies/date_type.json b/app/models/schemas/doi/controlled_vocabularies/date_type.json new file mode 100644 index 000000000..55a6a06cd --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/date_type.json @@ -0,0 +1,26 @@ +{ + "type": ["string", "null"], + "$schema": "https://json-schema.org/draft/2020-12/schema", + "anyOf": [ + { + "type": "string", + "enum": [ + "Accepted", + "Available", + "Copyrighted", + "Collected", + "Coverage", + "Created", + "Issued", + "Submitted", + "Updated", + "Valid", + "Withdrawn", + "Other" + ] + }, + { + "type": "null" + } + ] +} \ No newline at end of file diff --git a/app/models/schemas/doi/dates.json b/app/models/schemas/doi/dates.json new file mode 100644 index 000000000..90ab9e2f3 --- /dev/null +++ b/app/models/schemas/doi/dates.json @@ -0,0 +1,87 @@ +{ + "type": "array", + "title": "Dates", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "items": { + "type": "object", + "properties": { + "date": { + "oneOf": [ + { + "title": "W3CDTF Date/Time format", + "description": "YYYY-MM-DD", + "type": "string", + "format": "date" + }, + { + "title": "W3CDTF Date/Time format", + "description": "YYYY-MM-DDThh:mm:ssTZD, YYYY-MM-DDThh:mm:ss.sTZD", + "type": "string", + "format": "date-time" + }, + { + "title": "W3CDTF Date/Time format", + "description": "YYYY | -YYYY to indicate BC", + "type": "string", + "pattern":"^-?[0-9]{4}$" + }, + { + "title": "W3CDTF Date/Time format", + "description": "YYYY-MM", + "type": "string", + "pattern": "^\\d{4}-(0[1-9]|1[0-2])$" + }, + { + "title": "W3CDTF Date/Time format", + "description": "YYYY-MM-DDThh:mmTZD", + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(Z|[+-]\\d{2}:\\d{2})?$" + }, + { + "title": "ISO 8601 Date Range", + "description": "ISO 8601 date range (start/end) where each date-time is YYYY-MM-DDThh:mm[+/-]hh:mm or Z, and either start or end can be omitted, but not both.", + "type": "string", + "pattern": "^(?:\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?:Z|[+\\-]\\d{2}:\\d{2}))?/(?:\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?:Z|[+\\-]\\d{2}:\\d{2}))?$" + }, + { + "title": "ISO 8601 Date Range", + "description": "ISO 8601 date range (start/end) where each date-time is YYYY-MM-DDThh:mm:ss.s[+/-]hh:mm or Z, and either start or end can be omitted, but not both.", + "type": "string", + "pattern": "^(?:(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2}))?/(?:(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2}))?))$" + }, + { + "title": "ISO 8601 Date Range", + "description": "ISO 8601 date Range YYYY-MM-DD/YYYY-MM-DD, and either start or end can be omitted, but not both", + "type": "string", + "pattern": "^((\\d{4}-\\d{2}-\\d{2}/)|(/\\d{4}-\\d{2}-\\d{2})|(\\d{4}-\\d{2}-\\d{2}/\\d{4}-\\d{2}-\\d{2}))$" + }, + { + "title": "ISO 8601 Date Range", + "description": "ISO 8601 date Range YYYY-MM/YYYY-MM, and either start or end can be omitted, but not both", + "type": "string", + "pattern": "^((\\d{4}-\\d{2}/\\d{4}-\\d{2})|(\\d{4}-\\d{2}/)|(/\\d{4}-\\d{2}))$" + }, + { + "title": "ISO 8601 Date Range", + "description": "ISO 8601 date Range YYYY/YYYY, and either start or end can be omitted, but not both", + "type": "string", + "pattern": "^(?:\\d{4}/\\d{4}|\\d{4}/|/\\d{4})$" + } + ] + }, + "dateType": { + "$ref": "controlled_vocabularies/date_type.json" + }, + "dateInformation": { + "type": "string" + } + }, + "required": [ + "date" + ], + "dependentRequired": { + "date": ["dateType"] + }, + "additionalProperties": false + } +} \ No newline at end of file From c431f7b240c149ace34ea2f06b5c6caa2ad5bcb9 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Wed, 19 Nov 2025 11:20:52 -0500 Subject: [PATCH 33/79] Update validation qualifier to only valid schemas. --- app/models/doi.rb | 10 +++++++++- app/validators/xml_schema_validator.rb | 10 +++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/models/doi.rb b/app/models/doi.rb index e39288047..cf6edf14a 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -4,6 +4,14 @@ require "benchmark" class Doi < ApplicationRecord +INVALID_SCHEMAS = %w[ + http://datacite.org/schema/kernel-2.1 + http://datacite.org/schema/kernel-2.2 + http://datacite.org/schema/kernel-3.0 + http://datacite.org/schema/kernel-3.1 + http://datacite.org/schema/kernel-3 + ].freeze + self.ignored_columns += [:publisher] PUBLISHER_JSON_SCHEMA = Rails.root.join("app", "models", "schemas", "doi", "publisher.json") audited only: %i[doi url creators contributors titles publisher_obj publication_year types descriptions container sizes formats version_info language dates identifiers related_identifiers related_items funding_references geo_locations rights_list subjects schema_version content_url landing_page aasm_state source reason] @@ -114,7 +122,7 @@ def validate_publisher_obj?(doi) end def validate_json_attribute?(attribute) - validatable? && !self[attribute].nil? + validatable? && self[attribute].present? && !INVALID_SCHEMAS.include?(self.schema_version) end def schema_file_path(schema_name) diff --git a/app/validators/xml_schema_validator.rb b/app/validators/xml_schema_validator.rb index 7705ac6c2..e09f16f38 100644 --- a/app/validators/xml_schema_validator.rb +++ b/app/validators/xml_schema_validator.rb @@ -40,13 +40,9 @@ def validate_each(record, _attribute, value) kernel = get_valid_kernel(record.schema_version) return false if kernel.blank? - invalid_schemas = %w[ - http://datacite.org/schema/kernel-2.1 - http://datacite.org/schema/kernel-2.2 - http://datacite.org/schema/kernel-3.0 - http://datacite.org/schema/kernel-3.1 - http://datacite.org/schema/kernel-3 - ] + + invalid_schemas = Doi::INVALID_SCHEMAS + if invalid_schemas.include?(record.schema_version) && !record.skip_schema_version_validation record.errors.add(:xml, "DOI #{record.uid}: Schema #{record.schema_version} is no longer supported") From 9036ca7c9ce340dc5b9b4397fd250d666571baf6 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Wed, 19 Nov 2025 11:56:46 -0500 Subject: [PATCH 34/79] Appease rubocop. --- app/models/doi.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/models/doi.rb b/app/models/doi.rb index cf6edf14a..2faa80842 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -4,13 +4,13 @@ require "benchmark" class Doi < ApplicationRecord -INVALID_SCHEMAS = %w[ - http://datacite.org/schema/kernel-2.1 - http://datacite.org/schema/kernel-2.2 - http://datacite.org/schema/kernel-3.0 - http://datacite.org/schema/kernel-3.1 - http://datacite.org/schema/kernel-3 - ].freeze + INVALID_SCHEMAS = %w[ + http://datacite.org/schema/kernel-2.1 + http://datacite.org/schema/kernel-2.2 + http://datacite.org/schema/kernel-3.0 + http://datacite.org/schema/kernel-3.1 + http://datacite.org/schema/kernel-3 + ].freeze self.ignored_columns += [:publisher] PUBLISHER_JSON_SCHEMA = Rails.root.join("app", "models", "schemas", "doi", "publisher.json") From 9663a7680f81aa4f2fc344dbaae93482bdea251e Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Wed, 19 Nov 2025 12:03:15 -0500 Subject: [PATCH 35/79] Appease rubocop. --- app/validators/xml_schema_validator.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/validators/xml_schema_validator.rb b/app/validators/xml_schema_validator.rb index e09f16f38..8f06dc466 100644 --- a/app/validators/xml_schema_validator.rb +++ b/app/validators/xml_schema_validator.rb @@ -40,9 +40,7 @@ def validate_each(record, _attribute, value) kernel = get_valid_kernel(record.schema_version) return false if kernel.blank? - - invalid_schemas = Doi::INVALID_SCHEMAS - + invalid_schemas = Doi::INVALID_SCHEMAS if invalid_schemas.include?(record.schema_version) && !record.skip_schema_version_validation record.errors.add(:xml, "DOI #{record.uid}: Schema #{record.schema_version} is no longer supported") From cdd8419b4ab09e85b3467bd79d2b53eb5b858080 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 1 Dec 2025 11:12:41 -0500 Subject: [PATCH 36/79] Backing out dates to string rather than verification using regular expressions. Commenting out a test due to a strong parameters issue. --- spec/requests/datacite_dois/patch_spec.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/spec/requests/datacite_dois/patch_spec.rb b/spec/requests/datacite_dois/patch_spec.rb index 64b881f5b..ec7136b8d 100644 --- a/spec/requests/datacite_dois/patch_spec.rb +++ b/spec/requests/datacite_dois/patch_spec.rb @@ -413,12 +413,13 @@ } end - it "error" do - patch "/dois/#{doi.doi}", valid_attributes, headers - - expect(last_response.status).to eq(422) - expect(json["errors"]).to eq([{ "source" => "titles", "title" => "Value at root is not an array", "uid" => "10.14454/4k3m-nyvg" }]) - end + # Temporarily comment out this test until the strong parameters issue can be fixed. + # it "error" do + # patch "/dois/#{doi.doi}", valid_attributes, headers + # + # expect(last_response.status).to eq(422) + # expect(json["errors"]).to eq([{ "source" => "titles", "title" => "Value at root is not an array", "uid" => "10.14454/4k3m-nyvg" }]) + # end end context "when the description is changed to empty" do From a08363c8662d5561a865c38e3ff61ca625dff544 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 1 Dec 2025 11:48:54 -0500 Subject: [PATCH 37/79] Remove validations for dates. Date to remain validated as string only for now. --- app/models/schemas/doi/dates.json | 63 +------------------------------ 1 file changed, 1 insertion(+), 62 deletions(-) diff --git a/app/models/schemas/doi/dates.json b/app/models/schemas/doi/dates.json index 90ab9e2f3..07b6c1609 100644 --- a/app/models/schemas/doi/dates.json +++ b/app/models/schemas/doi/dates.json @@ -6,68 +6,7 @@ "type": "object", "properties": { "date": { - "oneOf": [ - { - "title": "W3CDTF Date/Time format", - "description": "YYYY-MM-DD", - "type": "string", - "format": "date" - }, - { - "title": "W3CDTF Date/Time format", - "description": "YYYY-MM-DDThh:mm:ssTZD, YYYY-MM-DDThh:mm:ss.sTZD", - "type": "string", - "format": "date-time" - }, - { - "title": "W3CDTF Date/Time format", - "description": "YYYY | -YYYY to indicate BC", - "type": "string", - "pattern":"^-?[0-9]{4}$" - }, - { - "title": "W3CDTF Date/Time format", - "description": "YYYY-MM", - "type": "string", - "pattern": "^\\d{4}-(0[1-9]|1[0-2])$" - }, - { - "title": "W3CDTF Date/Time format", - "description": "YYYY-MM-DDThh:mmTZD", - "type": "string", - "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(Z|[+-]\\d{2}:\\d{2})?$" - }, - { - "title": "ISO 8601 Date Range", - "description": "ISO 8601 date range (start/end) where each date-time is YYYY-MM-DDThh:mm[+/-]hh:mm or Z, and either start or end can be omitted, but not both.", - "type": "string", - "pattern": "^(?:\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?:Z|[+\\-]\\d{2}:\\d{2}))?/(?:\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?:Z|[+\\-]\\d{2}:\\d{2}))?$" - }, - { - "title": "ISO 8601 Date Range", - "description": "ISO 8601 date range (start/end) where each date-time is YYYY-MM-DDThh:mm:ss.s[+/-]hh:mm or Z, and either start or end can be omitted, but not both.", - "type": "string", - "pattern": "^(?:(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2}))?/(?:(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2}))?))$" - }, - { - "title": "ISO 8601 Date Range", - "description": "ISO 8601 date Range YYYY-MM-DD/YYYY-MM-DD, and either start or end can be omitted, but not both", - "type": "string", - "pattern": "^((\\d{4}-\\d{2}-\\d{2}/)|(/\\d{4}-\\d{2}-\\d{2})|(\\d{4}-\\d{2}-\\d{2}/\\d{4}-\\d{2}-\\d{2}))$" - }, - { - "title": "ISO 8601 Date Range", - "description": "ISO 8601 date Range YYYY-MM/YYYY-MM, and either start or end can be omitted, but not both", - "type": "string", - "pattern": "^((\\d{4}-\\d{2}/\\d{4}-\\d{2})|(\\d{4}-\\d{2}/)|(/\\d{4}-\\d{2}))$" - }, - { - "title": "ISO 8601 Date Range", - "description": "ISO 8601 date Range YYYY/YYYY, and either start or end can be omitted, but not both", - "type": "string", - "pattern": "^(?:\\d{4}/\\d{4}|\\d{4}/|/\\d{4})$" - } - ] + "type": "string" }, "dateType": { "$ref": "controlled_vocabularies/date_type.json" From 225deaed8165ed3f8cae6e41aa5517526f76ad00 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 1 Dec 2025 14:42:15 -0500 Subject: [PATCH 38/79] Add json-schema validation for resourceType. --- app/models/doi.rb | 1 + .../resource_type_general.json | 39 +++++++++++++++++++ app/models/schemas/doi/resource_type.json | 15 +++++++ 3 files changed, 55 insertions(+) create mode 100644 app/models/schemas/doi/controlled_vocabularies/resource_type_general.json create mode 100644 app/models/schemas/doi/resource_type.json diff --git a/app/models/doi.rb b/app/models/doi.rb index 4c499572f..2d24ba848 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -163,6 +163,7 @@ def schema_file_path(schema_name) validates :contributors, if: proc { |doi| doi.validate_json_attribute?(:contributors) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("contributors") } }, unless: :only_validate validates :subjects, if: proc { |doi| doi.validate_json_attribute?(:subjects) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("subjects") } }, unless: :only_validate validates :dates, if: proc { |doi| doi.validate_json_attribute?(:dates) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("dates") } }, unless: :only_validate + validates :resource_type, if: proc { |doi| doi.validate_json_attribute?(:resource_type) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("resource_type") } }, unless: :only_validate after_commit :update_url, on: %i[create update] after_commit :update_media, on: %i[create update] diff --git a/app/models/schemas/doi/controlled_vocabularies/resource_type_general.json b/app/models/schemas/doi/controlled_vocabularies/resource_type_general.json new file mode 100644 index 000000000..93f2875fe --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/resource_type_general.json @@ -0,0 +1,39 @@ +{ + "type": "string", + "title": "ResourceTypeGeneral", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "enum": [ + "Audiovisual", + "Award", + "Book", + "BookChapter", + "Collection", + "ComputationalNotebook", + "ConferencePaper", + "ConferenceProceeding", + "DataPaper", + "Dataset", + "Dissertation", + "Event", + "Image", + "InteractiveResource", + "Instrument", + "Journal", + "JournalArticle", + "Model", + "OutputManagementPlan", + "PeerReview", + "PhysicalObject", + "Preprint", + "Project", + "Report", + "Service", + "Software", + "Sound", + "Standard", + "StudyRegistration", + "Text", + "Workflow", + "Other" + ] +} \ No newline at end of file diff --git a/app/models/schemas/doi/resource_type.json b/app/models/schemas/doi/resource_type.json new file mode 100644 index 000000000..e7a97c3e8 --- /dev/null +++ b/app/models/schemas/doi/resource_type.json @@ -0,0 +1,15 @@ +{ + "type": "object", + "title": "ResourceType", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "resourceType": { "type": "string" }, + "resourceTypeGeneral": { "$ref": "controlled_vocabularies/resource_type_general.json" } + }, + "required": [ + "resourceType", + "resourceTypeGeneral" + ], + "additionalProperties": false, + "minitems": 1 +} From c2bba5b12115a4d41d4579058a81557438c76f55 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 1 Dec 2025 14:43:21 -0500 Subject: [PATCH 39/79] Revert test. --- spec/requests/datacite_dois/patch_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/requests/datacite_dois/patch_spec.rb b/spec/requests/datacite_dois/patch_spec.rb index ec7136b8d..50e1d6cbc 100644 --- a/spec/requests/datacite_dois/patch_spec.rb +++ b/spec/requests/datacite_dois/patch_spec.rb @@ -418,7 +418,7 @@ # patch "/dois/#{doi.doi}", valid_attributes, headers # # expect(last_response.status).to eq(422) - # expect(json["errors"]).to eq([{ "source" => "titles", "title" => "Value at root is not an array", "uid" => "10.14454/4k3m-nyvg" }]) + # expect(json["errors"]).to eq([{ "source" => "titles", "title" => "Title 'Submitted chemical data for InChIKey=YAPQBXQYLJRXSA-UHFFFAOYSA-N' should be an object instead of a string.", "uid" => "10.14454/4k3m-nyvg" }]) # end end From 3f6606c739219c73f6944902afc2c61519923253 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 1 Dec 2025 15:36:51 -0500 Subject: [PATCH 40/79] Add json-schema validation for alternageIdentifiers. --- app/models/doi.rb | 1 + .../schemas/doi/alternateIdentifiers.json | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 app/models/schemas/doi/alternateIdentifiers.json diff --git a/app/models/doi.rb b/app/models/doi.rb index 2d24ba848..5336c2cfc 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -164,6 +164,7 @@ def schema_file_path(schema_name) validates :subjects, if: proc { |doi| doi.validate_json_attribute?(:subjects) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("subjects") } }, unless: :only_validate validates :dates, if: proc { |doi| doi.validate_json_attribute?(:dates) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("dates") } }, unless: :only_validate validates :resource_type, if: proc { |doi| doi.validate_json_attribute?(:resource_type) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("resource_type") } }, unless: :only_validate + validates :alternate_identifiers, if: proc { |doi| doi.validate_json_attribute?(:alternate_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("alternate_identifiers") } }, unless: :only_validate after_commit :update_url, on: %i[create update] after_commit :update_media, on: %i[create update] diff --git a/app/models/schemas/doi/alternateIdentifiers.json b/app/models/schemas/doi/alternateIdentifiers.json new file mode 100644 index 000000000..173c5591d --- /dev/null +++ b/app/models/schemas/doi/alternateIdentifiers.json @@ -0,0 +1,24 @@ +{ + "type": "array", + "title": "AlternateIdentifiers", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "items": { + "type": "object", + "properties": { + "alternateIdentifier": { + "type": "string" + }, + "alternateIdentifierType": { + "type": "string" + } + }, + "additionalProperties": false, + "dependentRequired": { + "date": ["alternateIdentifierType"] + }, + "required": [ + "name" + ] + }, + "minItems": 0 +} \ No newline at end of file From 631550599bb48d726d45399b5e248f34c3293004 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 1 Dec 2025 23:34:23 -0500 Subject: [PATCH 41/79] add json-schema validation for relatedIdentifiers. --- app/models/doi.rb | 1 + .../related_identifier_type.json | 28 +++++++++ .../relation_type.json | 45 ++++++++++++++ .../schemas/doi/related_identifiers.json | 61 +++++++++++++++++++ 4 files changed, 135 insertions(+) create mode 100644 app/models/schemas/doi/controlled_vocabularies/related_identifier_type.json create mode 100644 app/models/schemas/doi/controlled_vocabularies/relation_type.json create mode 100644 app/models/schemas/doi/related_identifiers.json diff --git a/app/models/doi.rb b/app/models/doi.rb index 5336c2cfc..91e6fbe63 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -165,6 +165,7 @@ def schema_file_path(schema_name) validates :dates, if: proc { |doi| doi.validate_json_attribute?(:dates) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("dates") } }, unless: :only_validate validates :resource_type, if: proc { |doi| doi.validate_json_attribute?(:resource_type) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("resource_type") } }, unless: :only_validate validates :alternate_identifiers, if: proc { |doi| doi.validate_json_attribute?(:alternate_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("alternate_identifiers") } }, unless: :only_validate + validates :related_identifiers, if: proc { |doi| doi.validate_json_attribute?(:related_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_identifiers") } }, unless: :only_validate after_commit :update_url, on: %i[create update] after_commit :update_media, on: %i[create update] diff --git a/app/models/schemas/doi/controlled_vocabularies/related_identifier_type.json b/app/models/schemas/doi/controlled_vocabularies/related_identifier_type.json new file mode 100644 index 000000000..8ef54b551 --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/related_identifier_type.json @@ -0,0 +1,28 @@ +{ + "title": "RelatedIdentifierType", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string", + "enum": [ + "ARK", + "arXiv", + "bibcode", + "CSTR", + "DOI", + "EAN13", + "EISSN", + "Handle", + "IGSN", + "ISBN", + "ISSN", + "ISTC", + "LISSN", + "LSID", + "PMID", + "PURL", + "RRID", + "UPC", + "URL", + "URN", + "w3id" + ] +} \ No newline at end of file diff --git a/app/models/schemas/doi/controlled_vocabularies/relation_type.json b/app/models/schemas/doi/controlled_vocabularies/relation_type.json new file mode 100644 index 000000000..b8aea5c96 --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/relation_type.json @@ -0,0 +1,45 @@ +{ + "title": "RelationType", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string", + "enum": [ + "IsCitedBy", + "Cites", + "IsSupplementTo", + "IsSupplementedBy", + "IsContinuedBy", + "Continues", + "IsDescribedBy", + "Describes", + "HasMetadata", + "IsMetadataFor", + "HasVersion", + "IsVersionOf", + "IsNewVersionOf", + "IsPreviousVersionOf", + "IsPartOf", + "HasPart", + "IsPublishedIn", + "IsReferencedBy", + "References", + "IsDocumentedBy", + "Documents", + "IsCompiledBy", + "Compiles", + "IsVariantFormOf", + "IsOriginalFormOf", + "IsIdenticalTo", + "IsReviewedBy", + "Reviews", + "IsDerivedFrom", + "IsSourceOf", + "IsRequiredBy", + "Requires", + "IsObsoletedBy", + "Obsoletes", + "IsCollectedBy", + "Collects", + "IsTranslationOf", + "HasTranslation" + ] +} \ No newline at end of file diff --git a/app/models/schemas/doi/related_identifiers.json b/app/models/schemas/doi/related_identifiers.json new file mode 100644 index 000000000..3cbb98823 --- /dev/null +++ b/app/models/schemas/doi/related_identifiers.json @@ -0,0 +1,61 @@ +{ + "type": "array", + "title": "RelatedIdentifiers", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "items": { + "type": "object", + "properties": { + "relatedIdentifier": { + "type": "string" + }, + "relationType": { + "$ref": "controlled_vocabularies/relation_type.json" + }, + "relatedIdentifierType": { + "$ref": "controlled_vocabularies/related_identifier_type.json" + }, + "relatedMetaDataScheme": { + "type": "string" + }, + "schemeUri": { + "type": "string" + }, + "resourceTypeGeneral": { + "$ref": "controlled_vocabularies/resource_type_general.json" + } + }, + "if": { + "properties": { + "relationType": { + "enum": ["HasMetadata", "IsMetadataFor"] + } + } + }, + "then": { + "properties": { + "relatedMetadataScheme": { + "type": "string" + }, + "schemeUri": { + "type": "string" + }, + "schemeType": { + "type": "string" + } + } + }, + "else": { + "not": { + "anyOf": [ + { "required": ["relatedMetadataScheme"] }, + { "required": ["schemeUri"] }, + { "required": ["schemeType"] } + ] + } + }, + "dependentRequired": { + "relatedIdentifier": ["relatedIdentifierType", "relationType"] + }, + "additionalProperties": false + } +} From 4bb4b5b30b96c72964c4e81fa866c056468a8117 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 1 Dec 2025 23:44:58 -0500 Subject: [PATCH 42/79] Add json-schema validation for sizes. --- app/models/schemas/doi/sizes.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 app/models/schemas/doi/sizes.json diff --git a/app/models/schemas/doi/sizes.json b/app/models/schemas/doi/sizes.json new file mode 100644 index 000000000..ac46ed4fa --- /dev/null +++ b/app/models/schemas/doi/sizes.json @@ -0,0 +1,8 @@ +{ + "title": "Sizes", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "items": { + "type": "string" + } +} From 2d87449b2646f1258b8671f69fa1cb6939f32303 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 1 Dec 2025 23:51:52 -0500 Subject: [PATCH 43/79] Add json-schema validation for formats. --- app/models/doi.rb | 2 ++ app/models/schemas/doi/formats.json | 8 ++++++++ 2 files changed, 10 insertions(+) create mode 100644 app/models/schemas/doi/formats.json diff --git a/app/models/doi.rb b/app/models/doi.rb index 91e6fbe63..66055c60e 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -166,6 +166,8 @@ def schema_file_path(schema_name) validates :resource_type, if: proc { |doi| doi.validate_json_attribute?(:resource_type) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("resource_type") } }, unless: :only_validate validates :alternate_identifiers, if: proc { |doi| doi.validate_json_attribute?(:alternate_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("alternate_identifiers") } }, unless: :only_validate validates :related_identifiers, if: proc { |doi| doi.validate_json_attribute?(:related_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_identifiers") } }, unless: :only_validate + validates :sizes, if: proc { |doi| doi.validate_json_attribute?(:sizes) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("sizes") } }, unless: :only_validate + validates :formats, if: proc { |doi| doi.validate_json_attribute?(:formats) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("formats") } }, unless: :only_validate after_commit :update_url, on: %i[create update] after_commit :update_media, on: %i[create update] diff --git a/app/models/schemas/doi/formats.json b/app/models/schemas/doi/formats.json new file mode 100644 index 000000000..8f83ecca9 --- /dev/null +++ b/app/models/schemas/doi/formats.json @@ -0,0 +1,8 @@ +{ + "title": "Formats", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "items": { + "type": "string" + } +} From 329ee8feb5b54aad9abd74825e856cbe6670f250 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Tue, 2 Dec 2025 00:00:38 -0500 Subject: [PATCH 44/79] Mods. --- .../doi/controlled_vocabularies/resource_type_general.json | 2 +- app/models/schemas/doi/related_identifiers.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/schemas/doi/controlled_vocabularies/resource_type_general.json b/app/models/schemas/doi/controlled_vocabularies/resource_type_general.json index 93f2875fe..43c815c95 100644 --- a/app/models/schemas/doi/controlled_vocabularies/resource_type_general.json +++ b/app/models/schemas/doi/controlled_vocabularies/resource_type_general.json @@ -1,7 +1,7 @@ { - "type": "string", "title": "ResourceTypeGeneral", "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string", "enum": [ "Audiovisual", "Award", diff --git a/app/models/schemas/doi/related_identifiers.json b/app/models/schemas/doi/related_identifiers.json index 3cbb98823..3ec996622 100644 --- a/app/models/schemas/doi/related_identifiers.json +++ b/app/models/schemas/doi/related_identifiers.json @@ -1,7 +1,7 @@ { - "type": "array", "title": "RelatedIdentifiers", "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", "items": { "type": "object", "properties": { From 04de852613dd47f0fdd3e9cbcfaaa3bfdba3e48e Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Tue, 2 Dec 2025 00:01:46 -0500 Subject: [PATCH 45/79] Add json-schema validation for version. --- app/models/schemas/doi/version.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 app/models/schemas/doi/version.json diff --git a/app/models/schemas/doi/version.json b/app/models/schemas/doi/version.json new file mode 100644 index 000000000..93529b692 --- /dev/null +++ b/app/models/schemas/doi/version.json @@ -0,0 +1,5 @@ +{ + "title": "Version", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string" +} From 44fb68e2129c92dafd587e9c104c30e65df34d5c Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Tue, 2 Dec 2025 00:24:48 -0500 Subject: [PATCH 46/79] Add json-schema validation for descriptions. --- app/models/doi.rb | 1 + .../description_type.json | 13 +++++++++++++ app/models/schemas/doi/descriptions.json | 18 ++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 app/models/schemas/doi/controlled_vocabularies/description_type.json create mode 100644 app/models/schemas/doi/descriptions.json diff --git a/app/models/doi.rb b/app/models/doi.rb index 66055c60e..c0938d497 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -168,6 +168,7 @@ def schema_file_path(schema_name) validates :related_identifiers, if: proc { |doi| doi.validate_json_attribute?(:related_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_identifiers") } }, unless: :only_validate validates :sizes, if: proc { |doi| doi.validate_json_attribute?(:sizes) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("sizes") } }, unless: :only_validate validates :formats, if: proc { |doi| doi.validate_json_attribute?(:formats) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("formats") } }, unless: :only_validate + validates :descriptions, if: proc { |doi| doi.validate_json_attribute?(:descriptions) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("descriptions") } }, unless: :only_validate after_commit :update_url, on: %i[create update] after_commit :update_media, on: %i[create update] diff --git a/app/models/schemas/doi/controlled_vocabularies/description_type.json b/app/models/schemas/doi/controlled_vocabularies/description_type.json new file mode 100644 index 000000000..4dbe1a046 --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/description_type.json @@ -0,0 +1,13 @@ +{ + "title": "DescriptionType", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string", + "enum": [ + "Abstract", + "Methods", + "SeriesInformation", + "TableOfContents", + "TechnicalInfo", + "Other" + ] +} \ No newline at end of file diff --git a/app/models/schemas/doi/descriptions.json b/app/models/schemas/doi/descriptions.json new file mode 100644 index 000000000..95c2dc762 --- /dev/null +++ b/app/models/schemas/doi/descriptions.json @@ -0,0 +1,18 @@ +{ + "title": "Descriptions", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "items": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "dependentRequired": { + "description": ["descriptionType"] + }, + "descriptionType": { "$ref": "controlled_vocabularies/description_type.json" }, + "lang": { "type": ["string", "null"] } + }, + "additionalProperties": false + } +} From 598906b2a2bd55f98897fddd4d5824abddd2649e Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Wed, 3 Dec 2025 16:56:44 -0500 Subject: [PATCH 47/79] Fix some errors. --- app/models/doi.rb | 3 ++- ...eIdentifiers.json => alternate_identifiers.json} | 9 +++------ app/models/schemas/doi/publisher.json | 1 + spec/requests/datacite_dois/patch_spec.rb | 13 ++++++------- 4 files changed, 12 insertions(+), 14 deletions(-) rename app/models/schemas/doi/{alternateIdentifiers.json => alternate_identifiers.json} (82%) diff --git a/app/models/doi.rb b/app/models/doi.rb index c0938d497..9000b78f1 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -122,7 +122,7 @@ def validate_publisher_obj?(doi) end def validate_json_attribute?(attribute) - validatable? && self[attribute].present? && !INVALID_SCHEMAS.include?(self.schema_version) + validatable? && !self[attribute].nil? && !INVALID_SCHEMAS.include?(self.schema_version) end def schema_file_path(schema_name) @@ -169,6 +169,7 @@ def schema_file_path(schema_name) validates :sizes, if: proc { |doi| doi.validate_json_attribute?(:sizes) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("sizes") } }, unless: :only_validate validates :formats, if: proc { |doi| doi.validate_json_attribute?(:formats) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("formats") } }, unless: :only_validate validates :descriptions, if: proc { |doi| doi.validate_json_attribute?(:descriptions) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("descriptions") } }, unless: :only_validate + # validates :rights_list, if: proc { |doi| doi.validate_json_attribute?(:rights_list) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("rights") } }, unless: :only_validate after_commit :update_url, on: %i[create update] after_commit :update_media, on: %i[create update] diff --git a/app/models/schemas/doi/alternateIdentifiers.json b/app/models/schemas/doi/alternate_identifiers.json similarity index 82% rename from app/models/schemas/doi/alternateIdentifiers.json rename to app/models/schemas/doi/alternate_identifiers.json index 173c5591d..255969e10 100644 --- a/app/models/schemas/doi/alternateIdentifiers.json +++ b/app/models/schemas/doi/alternate_identifiers.json @@ -1,7 +1,7 @@ { - "type": "array", "title": "AlternateIdentifiers", "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", "items": { "type": "object", "properties": { @@ -14,11 +14,8 @@ }, "additionalProperties": false, "dependentRequired": { - "date": ["alternateIdentifierType"] - }, - "required": [ - "name" - ] + "alternateIdentifier": ["alternateIdentifierType"] + } }, "minItems": 0 } \ No newline at end of file diff --git a/app/models/schemas/doi/publisher.json b/app/models/schemas/doi/publisher.json index f8b1c66f4..1e9cfb9ec 100644 --- a/app/models/schemas/doi/publisher.json +++ b/app/models/schemas/doi/publisher.json @@ -1,4 +1,5 @@ { + "title": "Publisher", "type": "object", "$schema": "http://json-schema.org/draft-04/schema#", "properties": { diff --git a/spec/requests/datacite_dois/patch_spec.rb b/spec/requests/datacite_dois/patch_spec.rb index 50e1d6cbc..64b881f5b 100644 --- a/spec/requests/datacite_dois/patch_spec.rb +++ b/spec/requests/datacite_dois/patch_spec.rb @@ -413,13 +413,12 @@ } end - # Temporarily comment out this test until the strong parameters issue can be fixed. - # it "error" do - # patch "/dois/#{doi.doi}", valid_attributes, headers - # - # expect(last_response.status).to eq(422) - # expect(json["errors"]).to eq([{ "source" => "titles", "title" => "Title 'Submitted chemical data for InChIKey=YAPQBXQYLJRXSA-UHFFFAOYSA-N' should be an object instead of a string.", "uid" => "10.14454/4k3m-nyvg" }]) - # end + it "error" do + patch "/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq([{ "source" => "titles", "title" => "Value at root is not an array", "uid" => "10.14454/4k3m-nyvg" }]) + end end context "when the description is changed to empty" do From acb17d442f213eb532c54ca57e48dec4e6443adb Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Wed, 3 Dec 2025 17:26:53 -0500 Subject: [PATCH 48/79] More fixes. --- app/models/doi.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/models/doi.rb b/app/models/doi.rb index 9000b78f1..632fe0b58 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -117,10 +117,6 @@ class Doi < ApplicationRecord validates_presence_of :doi validates_presence_of :url, if: Proc.new { |doi| doi.is_registered_or_findable? } - def validate_publisher_obj?(doi) - doi.validatable? && doi.publisher_obj? && !(doi.publisher_obj.blank? || doi.publisher_obj.all?(nil)) - end - def validate_json_attribute?(attribute) validatable? && !self[attribute].nil? && !INVALID_SCHEMAS.include?(self.schema_version) end @@ -156,7 +152,7 @@ def schema_file_path(schema_name) # json-schema validation validates :publication_year, if: proc { |doi| doi.validate_json_attribute?(:publication_year) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("publication_year") } }, unless: :only_validate - validates :publisher_obj, if: ->(doi) { validate_publisher_obj?(doi) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("publisher") } }, unless: :only_validate + validates :publisher_obj, if: proc { |doi| doi.validate_json_attribute?(:publisher_obj) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("publisher") } }, unless: :only_validate validates :titles, if: proc { |doi| doi.validate_json_attribute?(:titles) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("titles") } }, unless: :only_validate # validates :doi, if: proc { |doi| doi.validate_json_attribute?(:doi) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("identifier") } }, unless: :only_validate validates :creators, if: proc { |doi| doi.validate_json_attribute?(:creators) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("creators") } }, unless: :only_validate From ad280461a50f68ee4191c5a76bdf673a2b597f58 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Wed, 3 Dec 2025 17:48:38 -0500 Subject: [PATCH 49/79] More fixes. --- app/models/doi.rb | 2 +- app/models/schemas/doi/contributors.json | 2 +- app/models/schemas/doi/creators.json | 2 +- app/models/schemas/doi/dates.json | 2 +- app/models/schemas/doi/descriptions.json | 1 + app/models/schemas/doi/publication_year.json | 4 ++-- app/models/schemas/doi/publisher.json | 2 +- app/models/schemas/doi/resource_type.json | 2 +- app/models/schemas/doi/rights_list.json | 17 +++++++++++++++++ app/models/schemas/doi/subjects.json | 2 +- app/models/schemas/doi/titles.json | 3 ++- 11 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 app/models/schemas/doi/rights_list.json diff --git a/app/models/doi.rb b/app/models/doi.rb index 632fe0b58..653a1ebec 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -165,7 +165,7 @@ def schema_file_path(schema_name) validates :sizes, if: proc { |doi| doi.validate_json_attribute?(:sizes) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("sizes") } }, unless: :only_validate validates :formats, if: proc { |doi| doi.validate_json_attribute?(:formats) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("formats") } }, unless: :only_validate validates :descriptions, if: proc { |doi| doi.validate_json_attribute?(:descriptions) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("descriptions") } }, unless: :only_validate - # validates :rights_list, if: proc { |doi| doi.validate_json_attribute?(:rights_list) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("rights") } }, unless: :only_validate + validates :rights_list, if: proc { |doi| doi.validate_json_attribute?(:rights_list) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("rights_list") } }, unless: :only_validate after_commit :update_url, on: %i[create update] after_commit :update_media, on: %i[create update] diff --git a/app/models/schemas/doi/contributors.json b/app/models/schemas/doi/contributors.json index 5d06a29db..041912c78 100644 --- a/app/models/schemas/doi/contributors.json +++ b/app/models/schemas/doi/contributors.json @@ -1,7 +1,7 @@ { - "type": "array", "title": "Contributors", "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", "items": { "type": "object", "properties": { diff --git a/app/models/schemas/doi/creators.json b/app/models/schemas/doi/creators.json index aa2dd9f5f..651269c9b 100644 --- a/app/models/schemas/doi/creators.json +++ b/app/models/schemas/doi/creators.json @@ -1,7 +1,7 @@ { - "type": "array", "title": "Creators", "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", "items": { "type": "object", "properties": { diff --git a/app/models/schemas/doi/dates.json b/app/models/schemas/doi/dates.json index 07b6c1609..294ce45fb 100644 --- a/app/models/schemas/doi/dates.json +++ b/app/models/schemas/doi/dates.json @@ -1,7 +1,7 @@ { - "type": "array", "title": "Dates", "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", "items": { "type": "object", "properties": { diff --git a/app/models/schemas/doi/descriptions.json b/app/models/schemas/doi/descriptions.json index 95c2dc762..ff2822518 100644 --- a/app/models/schemas/doi/descriptions.json +++ b/app/models/schemas/doi/descriptions.json @@ -1,6 +1,7 @@ { "title": "Descriptions", "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", "items": { "type": "object", "properties": { diff --git a/app/models/schemas/doi/publication_year.json b/app/models/schemas/doi/publication_year.json index 9134a5406..71f642461 100644 --- a/app/models/schemas/doi/publication_year.json +++ b/app/models/schemas/doi/publication_year.json @@ -1,7 +1,7 @@ { - "type": [ "integer", "string" ], - "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "publicationYear", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": [ "integer", "string" ], "oneOf": [ { "type": "string", diff --git a/app/models/schemas/doi/publisher.json b/app/models/schemas/doi/publisher.json index 1e9cfb9ec..174810103 100644 --- a/app/models/schemas/doi/publisher.json +++ b/app/models/schemas/doi/publisher.json @@ -1,7 +1,7 @@ { "title": "Publisher", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", - "$schema": "http://json-schema.org/draft-04/schema#", "properties": { "name": { "type": "string" }, "publisherIdentifier": { "type": "string" }, diff --git a/app/models/schemas/doi/resource_type.json b/app/models/schemas/doi/resource_type.json index e7a97c3e8..d1b7550dd 100644 --- a/app/models/schemas/doi/resource_type.json +++ b/app/models/schemas/doi/resource_type.json @@ -1,7 +1,7 @@ { - "type": "object", "title": "ResourceType", "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", "properties": { "resourceType": { "type": "string" }, "resourceTypeGeneral": { "$ref": "controlled_vocabularies/resource_type_general.json" } diff --git a/app/models/schemas/doi/rights_list.json b/app/models/schemas/doi/rights_list.json new file mode 100644 index 000000000..d3f70ce4c --- /dev/null +++ b/app/models/schemas/doi/rights_list.json @@ -0,0 +1,17 @@ +{ + "title": "RightsList", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "items": { + "type": "object", + "properties": { + "rights": { "type": "string" }, + "rightsUri": { "type": "string" }, + "rightsIdentifier": { "type": "string" }, + "rightsIdentifierScheme": { "type": "string" }, + "schemeUri": { "type": "string" }, + "lang": { "type": "string" } + }, + "additionalProperties": false + } +} diff --git a/app/models/schemas/doi/subjects.json b/app/models/schemas/doi/subjects.json index b7af6d634..8032b5e2f 100644 --- a/app/models/schemas/doi/subjects.json +++ b/app/models/schemas/doi/subjects.json @@ -1,7 +1,7 @@ { - "type": "array", "title": "Subjects", "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", "minItems": 0, "items": { "type": "object", diff --git a/app/models/schemas/doi/titles.json b/app/models/schemas/doi/titles.json index eedc5938e..6220ada75 100644 --- a/app/models/schemas/doi/titles.json +++ b/app/models/schemas/doi/titles.json @@ -1,6 +1,7 @@ { - "type": "array", + "title": "Titles", "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", "items": { "type": "object", "properties": { From c6e093f20a9676b981ff92063898be9c200c5213 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Wed, 3 Dec 2025 17:58:57 -0500 Subject: [PATCH 50/79] Fix a test. --- spec/concerns/indexable_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/concerns/indexable_spec.rb b/spec/concerns/indexable_spec.rb index 05a0e86d7..640ff8318 100644 --- a/spec/concerns/indexable_spec.rb +++ b/spec/concerns/indexable_spec.rb @@ -71,7 +71,7 @@ :doi, titles: [ { title: "Soil investigations" } ], publisher: "Pangaea", - descriptions: { description: "this is a description" }, + descriptions: [{ description: "this is a description" }], aasm_state: "findable", ) end From da5f9e8a3ae96ee5b7921e6c2e1a13aa00c6bd9d Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Thu, 4 Dec 2025 10:10:28 -0500 Subject: [PATCH 51/79] Corrections plus new controlled vocabularies. --- .../contributor_type.json | 3 +- .../controlled_vocabularies/date_type.json | 3 +- .../controlled_vocabularies/name_type.json | 3 +- .../related_item_identifier_type.json | 28 +++++++++++ .../related_item_type.json | 47 +++++++++++++++++++ .../controlled_vocabularies/title_type.json | 3 +- 6 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 app/models/schemas/doi/controlled_vocabularies/related_item_identifier_type.json create mode 100644 app/models/schemas/doi/controlled_vocabularies/related_item_type.json diff --git a/app/models/schemas/doi/controlled_vocabularies/contributor_type.json b/app/models/schemas/doi/controlled_vocabularies/contributor_type.json index 637f7b41e..798417861 100644 --- a/app/models/schemas/doi/controlled_vocabularies/contributor_type.json +++ b/app/models/schemas/doi/controlled_vocabularies/contributor_type.json @@ -1,6 +1,7 @@ { - "type": ["string", "null"], + "title": "ContributorType", "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": ["string", "null"], "anyOf": [ { "type": "string", diff --git a/app/models/schemas/doi/controlled_vocabularies/date_type.json b/app/models/schemas/doi/controlled_vocabularies/date_type.json index 55a6a06cd..775d4e9c2 100644 --- a/app/models/schemas/doi/controlled_vocabularies/date_type.json +++ b/app/models/schemas/doi/controlled_vocabularies/date_type.json @@ -1,6 +1,7 @@ { - "type": ["string", "null"], + "title": "DateType", "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": ["string", "null"], "anyOf": [ { "type": "string", diff --git a/app/models/schemas/doi/controlled_vocabularies/name_type.json b/app/models/schemas/doi/controlled_vocabularies/name_type.json index f058cacdc..8d8f4e276 100644 --- a/app/models/schemas/doi/controlled_vocabularies/name_type.json +++ b/app/models/schemas/doi/controlled_vocabularies/name_type.json @@ -1,6 +1,7 @@ { - "type": ["string", "null"], + "title": "NameType", "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": ["string", "null"], "anyOf": [ { "type": "string", diff --git a/app/models/schemas/doi/controlled_vocabularies/related_item_identifier_type.json b/app/models/schemas/doi/controlled_vocabularies/related_item_identifier_type.json new file mode 100644 index 000000000..716a0f961 --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/related_item_identifier_type.json @@ -0,0 +1,28 @@ +{ + "title": "RelatedItemIdentifierType", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string", + "enum": [ + "ARK", + "arXiv", + "bibcode", + "CSTR", + "DOI", + "EAN13", + "EISSN", + "Handle", + "IGSN", + "ISBN", + "ISSN", + "ISTC", + "LISSN", + "LSID", + "PMID", + "PURL", + "RRID", + "UPC", + "URL", + "URN", + "w3id" + ] +} \ No newline at end of file diff --git a/app/models/schemas/doi/controlled_vocabularies/related_item_type.json b/app/models/schemas/doi/controlled_vocabularies/related_item_type.json new file mode 100644 index 000000000..79f772fe1 --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/related_item_type.json @@ -0,0 +1,47 @@ +{ + "title": "RelatedItemType", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": ["string", "null"], + "anyOf": [ + { + "type": "string", + "enum": [ + "Audiovisual", + "Award", + "Book", + "BookChapter", + "Collection", + "ComputationalNotebook", + "ConferencePaper", + "ConferenceProceeding", + "DataPaper", + "Dataset", + "Dissertation", + "Event", + "Image", + "InteractiveResource", + "Instrument", + "Journal", + "JournalArticle", + "Model", + "OutputManagementPlan", + "PeerReview", + "PhysicalObject", + "Preprint", + "Project", + "Report", + "Service", + "Software", + "Sound", + "Standard", + "StudyRegistration", + "Text", + "Workflow", + "Other" + ] + }, + { + "type": "null" + } + ] +} \ No newline at end of file diff --git a/app/models/schemas/doi/controlled_vocabularies/title_type.json b/app/models/schemas/doi/controlled_vocabularies/title_type.json index 37aefbdcb..957caa6fc 100644 --- a/app/models/schemas/doi/controlled_vocabularies/title_type.json +++ b/app/models/schemas/doi/controlled_vocabularies/title_type.json @@ -1,6 +1,7 @@ { - "type": ["string", "null"], + "title": "TitleType", "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": ["string", "null"], "anyOf": [ { "type": "string", From 111ec556fb86ce6824cf52061c95d1910551df6b Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 8 Dec 2025 11:55:04 -0500 Subject: [PATCH 52/79] Restructuring and corrections. --- app/models/doi.rb | 19 ++-- app/models/schemas/doi/affiliation.json | 23 +++++ app/models/schemas/doi/affiliations.json | 9 ++ .../schemas/doi/alternate_identifier.json | 17 ++++ .../schemas/doi/alternate_identifiers.json | 18 +--- app/models/schemas/doi/contributor.json | 40 +++++++++ app/models/schemas/doi/contributors.json | 38 +------- .../controlled_vocabularies/number_type.json | 19 ++++ app/models/schemas/doi/creator.json | 30 +++++++ app/models/schemas/doi/creators.json | 31 +------ app/models/schemas/doi/description.json | 16 ++++ app/models/schemas/doi/descriptions.json | 15 +--- app/models/schemas/doi/format.json | 5 ++ app/models/schemas/doi/formats.json | 2 +- app/models/schemas/doi/identifier.json | 7 ++ app/models/schemas/doi/name_identifier.json | 20 +++++ app/models/schemas/doi/name_identifiers.json | 9 ++ .../schemas/doi/related_identifier.json | 58 +++++++++++++ .../schemas/doi/related_identifiers.json | 57 +----------- app/models/schemas/doi/related_item.json | 83 ++++++++++++++++++ app/models/schemas/doi/related_items.json | 86 +++++++++++++++++++ app/models/schemas/doi/rights.json | 14 +++ app/models/schemas/doi/rights_list.json | 13 +-- .../schemas/doi/shared/affiliations.json | 26 ------ .../schemas/doi/shared/name_identifiers.json | 23 ----- app/models/schemas/doi/size.json | 5 ++ app/models/schemas/doi/sizes.json | 5 +- app/models/schemas/doi/subject.json | 31 +++++++ app/models/schemas/doi/subjects.json | 28 +----- app/models/schemas/doi/title.json | 16 ++++ app/models/schemas/doi/titles.json | 19 +--- 31 files changed, 523 insertions(+), 259 deletions(-) create mode 100644 app/models/schemas/doi/affiliation.json create mode 100644 app/models/schemas/doi/affiliations.json create mode 100644 app/models/schemas/doi/alternate_identifier.json create mode 100644 app/models/schemas/doi/contributor.json create mode 100644 app/models/schemas/doi/controlled_vocabularies/number_type.json create mode 100644 app/models/schemas/doi/creator.json create mode 100644 app/models/schemas/doi/description.json create mode 100644 app/models/schemas/doi/format.json create mode 100644 app/models/schemas/doi/identifier.json create mode 100644 app/models/schemas/doi/name_identifier.json create mode 100644 app/models/schemas/doi/name_identifiers.json create mode 100644 app/models/schemas/doi/related_identifier.json create mode 100644 app/models/schemas/doi/related_item.json create mode 100644 app/models/schemas/doi/related_items.json create mode 100644 app/models/schemas/doi/rights.json delete mode 100644 app/models/schemas/doi/shared/affiliations.json delete mode 100644 app/models/schemas/doi/shared/name_identifiers.json create mode 100644 app/models/schemas/doi/size.json create mode 100644 app/models/schemas/doi/subject.json create mode 100644 app/models/schemas/doi/title.json diff --git a/app/models/doi.rb b/app/models/doi.rb index 653a1ebec..34157e4f1 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -150,22 +150,27 @@ def schema_file_path(schema_name) validate :check_geo_locations, if: :geo_locations? validate :check_language, if: :language? - # json-schema validation - validates :publication_year, if: proc { |doi| doi.validate_json_attribute?(:publication_year) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("publication_year") } }, unless: :only_validate - validates :publisher_obj, if: proc { |doi| doi.validate_json_attribute?(:publisher_obj) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("publisher") } }, unless: :only_validate - validates :titles, if: proc { |doi| doi.validate_json_attribute?(:titles) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("titles") } }, unless: :only_validate - # validates :doi, if: proc { |doi| doi.validate_json_attribute?(:doi) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("identifier") } }, unless: :only_validate + # JSON-SCHEMA VALIDATION + # validates :identifier, if: proc { |doi| doi.validate_json_attribute?(:identifier) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("identifier") } }, unless: :only_validate validates :creators, if: proc { |doi| doi.validate_json_attribute?(:creators) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("creators") } }, unless: :only_validate - validates :contributors, if: proc { |doi| doi.validate_json_attribute?(:contributors) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("contributors") } }, unless: :only_validate + validates :titles, if: proc { |doi| doi.validate_json_attribute?(:titles) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("titles") } }, unless: :only_validate + validates :publisher_obj, if: proc { |doi| doi.validate_json_attribute?(:publisher_obj) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("publisher") } }, unless: :only_validate + validates :publication_year, if: proc { |doi| doi.validate_json_attribute?(:publication_year) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("publication_year") } }, unless: :only_validate validates :subjects, if: proc { |doi| doi.validate_json_attribute?(:subjects) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("subjects") } }, unless: :only_validate + validates :contributors, if: proc { |doi| doi.validate_json_attribute?(:contributors) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("contributors") } }, unless: :only_validate validates :dates, if: proc { |doi| doi.validate_json_attribute?(:dates) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("dates") } }, unless: :only_validate + # validates :language, if: proc { |doi| doi.validate_json_attribute?(:language) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("language") } }, unless: :only_validate validates :resource_type, if: proc { |doi| doi.validate_json_attribute?(:resource_type) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("resource_type") } }, unless: :only_validate validates :alternate_identifiers, if: proc { |doi| doi.validate_json_attribute?(:alternate_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("alternate_identifiers") } }, unless: :only_validate validates :related_identifiers, if: proc { |doi| doi.validate_json_attribute?(:related_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_identifiers") } }, unless: :only_validate validates :sizes, if: proc { |doi| doi.validate_json_attribute?(:sizes) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("sizes") } }, unless: :only_validate validates :formats, if: proc { |doi| doi.validate_json_attribute?(:formats) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("formats") } }, unless: :only_validate - validates :descriptions, if: proc { |doi| doi.validate_json_attribute?(:descriptions) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("descriptions") } }, unless: :only_validate + # validates :version, if: proc { |doi| doi.validate_json_attribute?(:version) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("version") } }, unless: :only_validate validates :rights_list, if: proc { |doi| doi.validate_json_attribute?(:rights_list) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("rights_list") } }, unless: :only_validate + validates :descriptions, if: proc { |doi| doi.validate_json_attribute?(:descriptions) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("descriptions") } }, unless: :only_validate + # validates :geolocations, if: proc { |doi| doi.validate_json_attribute?(:geolocations) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("geolocations") } }, unless: :only_validate + # validates :funding_references, if: proc { |doi| doi.validate_json_attribute?(:funding_references) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("funding_references") } }, unless: :only_validate + validates :related_items, if: proc { |doi| doi.validate_json_attribute?(:related_items) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_items") } }, unless: :only_validate after_commit :update_url, on: %i[create update] after_commit :update_media, on: %i[create update] diff --git a/app/models/schemas/doi/affiliation.json b/app/models/schemas/doi/affiliation.json new file mode 100644 index 000000000..3d6e46600 --- /dev/null +++ b/app/models/schemas/doi/affiliation.json @@ -0,0 +1,23 @@ +{ + "title": "Affiliation", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "affiliationIdentifier": { + "type": ["string", "null"] + }, + "affiliationIdentifierScheme": { + "type": ["string", "null"] + }, + "name": { + "type": ["string", "null"] + }, + "schemeUri": { + "type": ["string", "null"] + } + }, + "dependentRequired": { + "affiliationIdentifier": ["affiliationIdentifierScheme"] + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/app/models/schemas/doi/affiliations.json b/app/models/schemas/doi/affiliations.json new file mode 100644 index 000000000..54149ea9c --- /dev/null +++ b/app/models/schemas/doi/affiliations.json @@ -0,0 +1,9 @@ +{ + "title": "Affiliations", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "minItems": 0, + "items": { + "$ref": "affiliation.json" + } +} \ No newline at end of file diff --git a/app/models/schemas/doi/alternate_identifier.json b/app/models/schemas/doi/alternate_identifier.json new file mode 100644 index 000000000..8303219d8 --- /dev/null +++ b/app/models/schemas/doi/alternate_identifier.json @@ -0,0 +1,17 @@ +{ + "title": "AlternateIdentifier", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "alternateIdentifier": { + "type": "string" + }, + "alternateIdentifierType": { + "type": "string" + } + }, + "additionalProperties": false, + "dependentRequired": { + "alternateIdentifier": ["alternateIdentifierType"] + } +} \ No newline at end of file diff --git a/app/models/schemas/doi/alternate_identifiers.json b/app/models/schemas/doi/alternate_identifiers.json index 255969e10..0dd4d7f77 100644 --- a/app/models/schemas/doi/alternate_identifiers.json +++ b/app/models/schemas/doi/alternate_identifiers.json @@ -2,20 +2,8 @@ "title": "AlternateIdentifiers", "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "array", + "minItems": 0, "items": { - "type": "object", - "properties": { - "alternateIdentifier": { - "type": "string" - }, - "alternateIdentifierType": { - "type": "string" - } - }, - "additionalProperties": false, - "dependentRequired": { - "alternateIdentifier": ["alternateIdentifierType"] - } - }, - "minItems": 0 + "$ref": "alternate_identifier.json" + } } \ No newline at end of file diff --git a/app/models/schemas/doi/contributor.json b/app/models/schemas/doi/contributor.json new file mode 100644 index 000000000..4348a6b7c --- /dev/null +++ b/app/models/schemas/doi/contributor.json @@ -0,0 +1,40 @@ +{ + "title": "Contributor", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "nameType": { + "$ref": "controlled_vocabularies/name_type.json" + }, + "givenName": { + "type": ["string", "null"] + }, + "familyName": { + "type": ["string", "null"] + }, + "contributorType": { + "oneOf": [ + { + "$ref": "controlled_vocabularies/contributor_type.json" + }, + { + "type": "null" + } + ] + }, + "lang": { "type": ["string", "null"] }, + "affiliation": { + "$ref": "affiliations.json" + }, + "nameIdentifiers": { + "$ref": "name_identifiers.json" + } + }, + "additionalProperties": false, + "required": [ + "name" + ] +} \ No newline at end of file diff --git a/app/models/schemas/doi/contributors.json b/app/models/schemas/doi/contributors.json index 041912c78..444c3fc2d 100644 --- a/app/models/schemas/doi/contributors.json +++ b/app/models/schemas/doi/contributors.json @@ -2,42 +2,8 @@ "title": "Contributors", "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "array", + "minItems": 0, "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "nameType": { - "$ref": "controlled_vocabularies/name_type.json" - }, - "givenName": { - "type": ["string", "null"] - }, - "familyName": { - "type": ["string", "null"] - }, - "contributorType": { - "oneOf": [ - { - "$ref": "controlled_vocabularies/contributor_type.json" - }, - { - "type": "null" - } - ] - }, - "lang": { "type": ["string", "null"] }, - "affiliation": { - "$ref": "shared/affiliations.json" - }, - "nameIdentifiers": { - "$ref": "shared/name_identifiers.json" - } - }, - "additionalProperties": false, - "required": [ - "name" - ] + "$ref": "contributor.json" } } \ No newline at end of file diff --git a/app/models/schemas/doi/controlled_vocabularies/number_type.json b/app/models/schemas/doi/controlled_vocabularies/number_type.json new file mode 100644 index 000000000..956207856 --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/number_type.json @@ -0,0 +1,19 @@ +{ + "title": "NumberType", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": ["string", "null"], + "anyOf": [ + { + "type": "string", + "enum": [ + "Article", + "Chapter", + "Report", + "Other" + ] + }, + { + "type": "null" + } + ] +} \ No newline at end of file diff --git a/app/models/schemas/doi/creator.json b/app/models/schemas/doi/creator.json new file mode 100644 index 000000000..76be06b93 --- /dev/null +++ b/app/models/schemas/doi/creator.json @@ -0,0 +1,30 @@ +{ + "title": "Creator", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "nameType": { + "$ref": "controlled_vocabularies/name_type.json" + }, + "givenName": { + "type": ["string", "null"] + }, + "familyName": { + "type": ["string", "null"] + }, + "lang": { "type": ["string", "null"] }, + "affiliation": { + "$ref": "affiliations.json" + }, + "nameIdentifiers": { + "$ref": "name_identifiers.json" + } + }, + "additionalProperties": false, + "required": [ + "name" + ] +} diff --git a/app/models/schemas/doi/creators.json b/app/models/schemas/doi/creators.json index 651269c9b..08af1f405 100644 --- a/app/models/schemas/doi/creators.json +++ b/app/models/schemas/doi/creators.json @@ -2,33 +2,8 @@ "title": "Creators", "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "array", + "minItems": 1, "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "nameType": { - "$ref": "controlled_vocabularies/name_type.json" - }, - "givenName": { - "type": ["string", "null"] - }, - "familyName": { - "type": ["string", "null"] - }, - "lang": { "type": ["string", "null"] }, - "affiliation": { - "$ref": "shared/affiliations.json" - }, - "nameIdentifiers": { - "$ref": "shared/name_identifiers.json" - } - }, - "additionalProperties": false, - "required": [ - "name" - ] - }, - "minItems": 1 + "$ref": "creator.json" + } } \ No newline at end of file diff --git a/app/models/schemas/doi/description.json b/app/models/schemas/doi/description.json new file mode 100644 index 000000000..ccdfe9808 --- /dev/null +++ b/app/models/schemas/doi/description.json @@ -0,0 +1,16 @@ +{ + "title": "Description", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "dependentRequired": { + "description": ["descriptionType"] + }, + "descriptionType": { "$ref": "controlled_vocabularies/description_type.json" }, + "lang": { "type": ["string", "null"] } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/app/models/schemas/doi/descriptions.json b/app/models/schemas/doi/descriptions.json index ff2822518..1158c6555 100644 --- a/app/models/schemas/doi/descriptions.json +++ b/app/models/schemas/doi/descriptions.json @@ -3,17 +3,6 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "array", "items": { - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "dependentRequired": { - "description": ["descriptionType"] - }, - "descriptionType": { "$ref": "controlled_vocabularies/description_type.json" }, - "lang": { "type": ["string", "null"] } - }, - "additionalProperties": false + "$ref": "description.json" } -} +} \ No newline at end of file diff --git a/app/models/schemas/doi/format.json b/app/models/schemas/doi/format.json new file mode 100644 index 000000000..5509c9938 --- /dev/null +++ b/app/models/schemas/doi/format.json @@ -0,0 +1,5 @@ +{ + "title": "Format", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string" +} diff --git a/app/models/schemas/doi/formats.json b/app/models/schemas/doi/formats.json index 8f83ecca9..1d647b0a5 100644 --- a/app/models/schemas/doi/formats.json +++ b/app/models/schemas/doi/formats.json @@ -3,6 +3,6 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "array", "items": { - "type": "string" + "$ref": "size.json" } } diff --git a/app/models/schemas/doi/identifier.json b/app/models/schemas/doi/identifier.json new file mode 100644 index 000000000..72001439e --- /dev/null +++ b/app/models/schemas/doi/identifier.json @@ -0,0 +1,7 @@ +{ + "title": "Doi", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string", + "pattern": "^.*$", + "description": "A DOI (Digital Object Identifier) registered by a DataCite Member. The format should be 10.21384/foo." +} \ No newline at end of file diff --git a/app/models/schemas/doi/name_identifier.json b/app/models/schemas/doi/name_identifier.json new file mode 100644 index 000000000..7ed3d8aa1 --- /dev/null +++ b/app/models/schemas/doi/name_identifier.json @@ -0,0 +1,20 @@ +{ + "title": "NameIdentifiers", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "schemeUri": { + "type": ["string", "null"] + }, + "nameIdentifier": { + "type": ["string", "null"] + }, + "nameIdentifierScheme": { + "type": ["string", "null"] + } + }, + "dependentRequired": { + "affiliationIdentifier": ["nameIdentifierScheme"] + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/app/models/schemas/doi/name_identifiers.json b/app/models/schemas/doi/name_identifiers.json new file mode 100644 index 000000000..855e3d0dc --- /dev/null +++ b/app/models/schemas/doi/name_identifiers.json @@ -0,0 +1,9 @@ +{ + "title": "NameIdentifiers", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "minItems": 0, + "items": { + "$ref": "name_identifier.json" + } +} \ No newline at end of file diff --git a/app/models/schemas/doi/related_identifier.json b/app/models/schemas/doi/related_identifier.json new file mode 100644 index 000000000..df4dc3705 --- /dev/null +++ b/app/models/schemas/doi/related_identifier.json @@ -0,0 +1,58 @@ +{ + "title": "RelatedIdentifiers", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "relatedIdentifier": { + "type": "string" + }, + "relationType": { + "$ref": "controlled_vocabularies/relation_type.json" + }, + "relatedIdentifierType": { + "$ref": "controlled_vocabularies/related_identifier_type.json" + }, + "relatedMetaDataScheme": { + "type": "string" + }, + "schemeUri": { + "type": "string" + }, + "resourceTypeGeneral": { + "$ref": "controlled_vocabularies/resource_type_general.json" + } + }, + "if": { + "properties": { + "relationType": { + "enum": ["HasMetadata", "IsMetadataFor"] + } + } + }, + "then": { + "properties": { + "relatedMetadataScheme": { + "type": "string" + }, + "schemeUri": { + "type": "string" + }, + "schemeType": { + "type": "string" + } + } + }, + "else": { + "not": { + "anyOf": [ + { "required": ["relatedMetadataScheme"] }, + { "required": ["schemeUri"] }, + { "required": ["schemeType"] } + ] + } + }, + "dependentRequired": { + "relatedIdentifier": ["relatedIdentifierType", "relationType"] + }, + "additionalProperties": false +} diff --git a/app/models/schemas/doi/related_identifiers.json b/app/models/schemas/doi/related_identifiers.json index 3ec996622..d52fc123d 100644 --- a/app/models/schemas/doi/related_identifiers.json +++ b/app/models/schemas/doi/related_identifiers.json @@ -3,59 +3,6 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "array", "items": { - "type": "object", - "properties": { - "relatedIdentifier": { - "type": "string" - }, - "relationType": { - "$ref": "controlled_vocabularies/relation_type.json" - }, - "relatedIdentifierType": { - "$ref": "controlled_vocabularies/related_identifier_type.json" - }, - "relatedMetaDataScheme": { - "type": "string" - }, - "schemeUri": { - "type": "string" - }, - "resourceTypeGeneral": { - "$ref": "controlled_vocabularies/resource_type_general.json" - } - }, - "if": { - "properties": { - "relationType": { - "enum": ["HasMetadata", "IsMetadataFor"] - } - } - }, - "then": { - "properties": { - "relatedMetadataScheme": { - "type": "string" - }, - "schemeUri": { - "type": "string" - }, - "schemeType": { - "type": "string" - } - } - }, - "else": { - "not": { - "anyOf": [ - { "required": ["relatedMetadataScheme"] }, - { "required": ["schemeUri"] }, - { "required": ["schemeType"] } - ] - } - }, - "dependentRequired": { - "relatedIdentifier": ["relatedIdentifierType", "relationType"] - }, - "additionalProperties": false + "$ref": "related_identifier.json" } -} +} \ No newline at end of file diff --git a/app/models/schemas/doi/related_item.json b/app/models/schemas/doi/related_item.json new file mode 100644 index 000000000..b2febd3f6 --- /dev/null +++ b/app/models/schemas/doi/related_item.json @@ -0,0 +1,83 @@ +{ + "title": "RelatedItem", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "relatedItemType": { + "$ref": "controlled_vocabularies/related_item_type.json" + }, + "relationType": { + "$ref": "controlled_vocabularies/relation_type.json" + }, + "relatedItemIdentifier": { + "type": "object", + "properties": { + "relatedItemIdentifier": { + "type": "string" + }, + "relatedItemIdentifierType": { + "$ref": "controlled_vocabularies/related_identifier_type.json" + }, + "relatedMetadataScheme": { + "type": "string" + }, + "schemeURI": { + "type": "string" + }, + "schemeType": { + "type": "string" + } + }, + "additionalProperties": false + }, + "creators": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "creator.json" + } + }, + "titles": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "title.json" + } + }, + "publicationYear": { + "$ref": "publication_year.json" + }, + "volume": { + "type": "string" + }, + "issue": { + "type": "string" + }, + "number": { + "type": "string" + }, + "numberType": { + "$ref": "controlled_vocabularies/number_type.json" + }, + "firstPage": { + "type": "string" + }, + "lastPage": { + "type": "string" + }, + "publisher": { + "type": "string" + }, + "edition": { + "type": "string" + }, + "contributors": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "contributor.json" + } + } + }, + "additionalProperties": true +} \ No newline at end of file diff --git a/app/models/schemas/doi/related_items.json b/app/models/schemas/doi/related_items.json new file mode 100644 index 000000000..7f0931582 --- /dev/null +++ b/app/models/schemas/doi/related_items.json @@ -0,0 +1,86 @@ +{ + "title": "RelatedItems", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "items": { + "type": "object", + "properties": { + "relatedItemType": { + "$ref": "controlled_vocabularies/related_item_type.json" + }, + "relationType": { + "$ref": "controlled_vocabularies/relation_type.json" + }, + "relatedItemIdentifier": { + "type": "object", + "properties": { + "relatedItemIdentifier": { + "type": "string" + }, + "relatedItemIdentifierType": { + "$ref": "controlled_vocabularies/related_identifier_type.json" + }, + "relatedMetadataScheme": { + "type": "string" + }, + "schemeURI": { + "type": "string" + }, + "schemeType": { + "type": "string" + } + }, + "additionalProperties": false + }, + "creators": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "creator.json" + } + }, + "titles": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "title.json" + } + }, + "publicationYear": { + "$ref": "publication_year.json" + }, + "volume": { + "type": "string" + }, + "issue": { + "type": "string" + }, + "number": { + "type": "string" + }, + "numberType": { + "$ref": "controlled_vocabularies/number_type.json" + }, + "firstPage": { + "type": "string" + }, + "lastPage": { + "type": "string" + }, + "publisher": { + "type": "string" + }, + "edition": { + "type": "string" + }, + "contributors": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "contributor.json" + } + } + }, + "additionalProperties": true + } +} \ No newline at end of file diff --git a/app/models/schemas/doi/rights.json b/app/models/schemas/doi/rights.json new file mode 100644 index 000000000..51e1b1fe0 --- /dev/null +++ b/app/models/schemas/doi/rights.json @@ -0,0 +1,14 @@ +{ + "title": "Rights", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "rights": { "type": "string" }, + "rightsUri": { "type": "string" }, + "rightsIdentifier": { "type": "string" }, + "rightsIdentifierScheme": { "type": "string" }, + "schemeUri": { "type": "string" }, + "lang": { "type": "string" } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/app/models/schemas/doi/rights_list.json b/app/models/schemas/doi/rights_list.json index d3f70ce4c..22528d106 100644 --- a/app/models/schemas/doi/rights_list.json +++ b/app/models/schemas/doi/rights_list.json @@ -3,15 +3,6 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "array", "items": { - "type": "object", - "properties": { - "rights": { "type": "string" }, - "rightsUri": { "type": "string" }, - "rightsIdentifier": { "type": "string" }, - "rightsIdentifierScheme": { "type": "string" }, - "schemeUri": { "type": "string" }, - "lang": { "type": "string" } - }, - "additionalProperties": false + "$ref": "rights.json" } -} +} \ No newline at end of file diff --git a/app/models/schemas/doi/shared/affiliations.json b/app/models/schemas/doi/shared/affiliations.json deleted file mode 100644 index 1f6d260bb..000000000 --- a/app/models/schemas/doi/shared/affiliations.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "type": "array", - "title": "Affiliations", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "items": { - "type": "object", - "properties": { - "affiliationIdentifier": { - "type": ["string", "null"] - }, - "affiliationIdentifierScheme": { - "type": ["string", "null"] - }, - "name": { - "type": ["string", "null"] - }, - "schemeUri": { - "type": ["string", "null"] - } - }, - "dependentRequired": { - "affiliationIdentifier": ["affiliationIdentifierScheme"] - }, - "additionalProperties": false - } -} \ No newline at end of file diff --git a/app/models/schemas/doi/shared/name_identifiers.json b/app/models/schemas/doi/shared/name_identifiers.json deleted file mode 100644 index 1d8013c96..000000000 --- a/app/models/schemas/doi/shared/name_identifiers.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "type": "array", - "title": "NameIdentifiers", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "items": { - "type": "object", - "properties": { - "schemeUri": { - "type": ["string", "null"] - }, - "nameIdentifier": { - "type": ["string", "null"] - }, - "nameIdentifierScheme": { - "type": ["string", "null"] - } - }, - "dependentRequired": { - "affiliationIdentifier": ["nameIdentifierScheme"] - }, - "additionalProperties": false - } -} \ No newline at end of file diff --git a/app/models/schemas/doi/size.json b/app/models/schemas/doi/size.json new file mode 100644 index 000000000..a95e0ee3a --- /dev/null +++ b/app/models/schemas/doi/size.json @@ -0,0 +1,5 @@ +{ + "title": "Size", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string" +} diff --git a/app/models/schemas/doi/sizes.json b/app/models/schemas/doi/sizes.json index ac46ed4fa..8f20fc865 100644 --- a/app/models/schemas/doi/sizes.json +++ b/app/models/schemas/doi/sizes.json @@ -2,7 +2,8 @@ "title": "Sizes", "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "array", + "minItems": 0, "items": { - "type": "string" + "$ref": "size.json" } -} +} \ No newline at end of file diff --git a/app/models/schemas/doi/subject.json b/app/models/schemas/doi/subject.json new file mode 100644 index 000000000..6802c652d --- /dev/null +++ b/app/models/schemas/doi/subject.json @@ -0,0 +1,31 @@ +{ + "title": "Subject", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "subject": { + "type": ["string", "null"] + }, + "schemeUri": { + "type": ["string", "null"], + "format": "uri" + }, + "valueUri": { + "type": ["string", "null"], + "format": "uri" + }, + "subjectScheme": { + "type": ["string", "null" ] + }, + "classificationCode": { + "type": ["string", "null" ] + }, + "lang": { + "type": ["string", "null" ] + } + }, + "required": [ + "subject" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/app/models/schemas/doi/subjects.json b/app/models/schemas/doi/subjects.json index 8032b5e2f..56b9add2e 100644 --- a/app/models/schemas/doi/subjects.json +++ b/app/models/schemas/doi/subjects.json @@ -4,32 +4,6 @@ "type": "array", "minItems": 0, "items": { - "type": "object", - "properties": { - "subject": { - "type": ["string", "null"] - }, - "schemeUri": { - "type": ["string", "null"], - "format": "uri" - }, - "valueUri": { - "type": ["string", "null"], - "format": "uri" - }, - "subjectScheme": { - "type": ["string", "null" ] - }, - "classificationCode": { - "type": ["string", "null" ] - }, - "lang": { - "type": ["string", "null" ] - } - }, - "required": [ - "subject" - ], - "additionalProperties": false + "$ref": "subject.json" } } \ No newline at end of file diff --git a/app/models/schemas/doi/title.json b/app/models/schemas/doi/title.json new file mode 100644 index 000000000..41aa19825 --- /dev/null +++ b/app/models/schemas/doi/title.json @@ -0,0 +1,16 @@ +{ + "title": "Title", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "titleType": { "$ref": "controlled_vocabularies/title_type.json" }, + "lang": { "type": ["string", "null"] } + }, + "required": [ + "title" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/app/models/schemas/doi/titles.json b/app/models/schemas/doi/titles.json index 6220ada75..7b1def10c 100644 --- a/app/models/schemas/doi/titles.json +++ b/app/models/schemas/doi/titles.json @@ -2,19 +2,8 @@ "title": "Titles", "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "array", + "minItems": 1, "items": { - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "titleType": { "$ref": "controlled_vocabularies/title_type.json" }, - "lang": { "type": ["string", "null"] } - }, - "required": [ - "title" - ], - "additionalProperties": false - }, - "minitems": 1 -} + "$ref": "title.json" + } +} \ No newline at end of file From c917c856d674b58b7a161ec941cca4a752a1432e Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 8 Dec 2025 13:01:31 -0500 Subject: [PATCH 53/79] Add language validation, make sure it is used in all references to language/lang. Fix some tests. --- app/models/schemas/client/subjects.json | 5 +---- app/models/schemas/doi/contributor.json | 4 +++- app/models/schemas/doi/creator.json | 5 +++-- app/models/schemas/doi/description.json | 10 +++++++--- app/models/schemas/doi/language.json | 5 +++++ app/models/schemas/doi/publisher.json | 4 +++- app/models/schemas/doi/rights.json | 4 +++- app/models/schemas/doi/subject.json | 2 +- app/models/schemas/doi/title.json | 8 ++++++-- spec/factories/doi.rb | 5 ++++- spec/requests/datacite_dois/post_spec.rb | 2 +- 11 files changed, 37 insertions(+), 17 deletions(-) create mode 100644 app/models/schemas/doi/language.json diff --git a/app/models/schemas/client/subjects.json b/app/models/schemas/client/subjects.json index 3e204ecd6..69f9da678 100644 --- a/app/models/schemas/client/subjects.json +++ b/app/models/schemas/client/subjects.json @@ -14,10 +14,7 @@ ] }, "lang": { - "type": [ - "string", - "null" - ] + "$ref": "language.json" }, "subject": { "type": "string" }, "subjectScheme": { "type": "string" } diff --git a/app/models/schemas/doi/contributor.json b/app/models/schemas/doi/contributor.json index 4348a6b7c..a28790b12 100644 --- a/app/models/schemas/doi/contributor.json +++ b/app/models/schemas/doi/contributor.json @@ -25,7 +25,9 @@ } ] }, - "lang": { "type": ["string", "null"] }, + "lang": { + "$ref": "language.json" + }, "affiliation": { "$ref": "affiliations.json" }, diff --git a/app/models/schemas/doi/creator.json b/app/models/schemas/doi/creator.json index 76be06b93..197c3c531 100644 --- a/app/models/schemas/doi/creator.json +++ b/app/models/schemas/doi/creator.json @@ -15,8 +15,9 @@ "familyName": { "type": ["string", "null"] }, - "lang": { "type": ["string", "null"] }, - "affiliation": { + "lang": { + "$ref": "language.json" + }, "affiliation": { "$ref": "affiliations.json" }, "nameIdentifiers": { diff --git a/app/models/schemas/doi/description.json b/app/models/schemas/doi/description.json index ccdfe9808..6fe24221c 100644 --- a/app/models/schemas/doi/description.json +++ b/app/models/schemas/doi/description.json @@ -6,11 +6,15 @@ "description": { "type": "string" }, + "descriptionType": { + "$ref": "controlled_vocabularies/description_type.json" + }, + "lang": { + "$ref": "language.json" + } + }, "dependentRequired": { "description": ["descriptionType"] - }, - "descriptionType": { "$ref": "controlled_vocabularies/description_type.json" }, - "lang": { "type": ["string", "null"] } }, "additionalProperties": false } \ No newline at end of file diff --git a/app/models/schemas/doi/language.json b/app/models/schemas/doi/language.json new file mode 100644 index 000000000..01032b8ab --- /dev/null +++ b/app/models/schemas/doi/language.json @@ -0,0 +1,5 @@ +{ + "title": "Language", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": ["string", "null" ] +} diff --git a/app/models/schemas/doi/publisher.json b/app/models/schemas/doi/publisher.json index 174810103..956c543e9 100644 --- a/app/models/schemas/doi/publisher.json +++ b/app/models/schemas/doi/publisher.json @@ -7,7 +7,9 @@ "publisherIdentifier": { "type": "string" }, "publisherIdentifierScheme": { "type": "string" }, "schemeUri": { "type": "string" }, - "lang": { "type": "string" } + "lang": { + "$ref": "language.json" + } }, "additionalProperties": false } diff --git a/app/models/schemas/doi/rights.json b/app/models/schemas/doi/rights.json index 51e1b1fe0..fbc046bd4 100644 --- a/app/models/schemas/doi/rights.json +++ b/app/models/schemas/doi/rights.json @@ -8,7 +8,9 @@ "rightsIdentifier": { "type": "string" }, "rightsIdentifierScheme": { "type": "string" }, "schemeUri": { "type": "string" }, - "lang": { "type": "string" } + "lang": { + "$ref": "language.json" + } }, "additionalProperties": false } \ No newline at end of file diff --git a/app/models/schemas/doi/subject.json b/app/models/schemas/doi/subject.json index 6802c652d..7fa2b7ee1 100644 --- a/app/models/schemas/doi/subject.json +++ b/app/models/schemas/doi/subject.json @@ -21,7 +21,7 @@ "type": ["string", "null" ] }, "lang": { - "type": ["string", "null" ] + "$ref": "language.json" } }, "required": [ diff --git a/app/models/schemas/doi/title.json b/app/models/schemas/doi/title.json index 41aa19825..bebf87244 100644 --- a/app/models/schemas/doi/title.json +++ b/app/models/schemas/doi/title.json @@ -6,8 +6,12 @@ "title": { "type": "string" }, - "titleType": { "$ref": "controlled_vocabularies/title_type.json" }, - "lang": { "type": ["string", "null"] } + "titleType": { + "$ref": "controlled_vocabularies/title_type.json" + }, + "lang": { + "$ref": "language.json" + } }, "required": [ "title" diff --git a/spec/factories/doi.rb b/spec/factories/doi.rb index 870ffc72b..a85d32e01 100644 --- a/spec/factories/doi.rb +++ b/spec/factories/doi.rb @@ -97,7 +97,10 @@ [{ "title": "Data from: A new malaria agent in African hominids." }] end descriptions do - [{ "description": "Data from: A new malaria agent in African hominids." }] + [{ + "description": "Data from: A new malaria agent in African hominids.", + "descriptionType": "TechnicalInfo" + }] end subjects do [ diff --git a/spec/requests/datacite_dois/post_spec.rb b/spec/requests/datacite_dois/post_spec.rb index 5547afa9c..f9d56692f 100644 --- a/spec/requests/datacite_dois/post_spec.rb +++ b/spec/requests/datacite_dois/post_spec.rb @@ -2194,7 +2194,7 @@ it "updates the Doi" do get "/dois/#{doi.doi}", nil, headers - expect(json.dig("data", "attributes", "descriptions")).to eq([{ "description" => "Data from: A new malaria agent in African hominids." }]) + expect(json.dig("data", "attributes", "descriptions")).to eq([{"description"=>"Data from: A new malaria agent in African hominids.", "descriptionType"=>"TechnicalInfo"}]) expect(json.dig("data", "attributes", "container")).to be_empty patch "/dois/#{doi.doi}", update_attributes, headers From e44889b44378ea32e8ba1e91860c6a0ceb6e89a1 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 8 Dec 2025 13:06:25 -0500 Subject: [PATCH 54/79] Appease rubocop. --- spec/requests/datacite_dois/post_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/requests/datacite_dois/post_spec.rb b/spec/requests/datacite_dois/post_spec.rb index f9d56692f..5f2666fb3 100644 --- a/spec/requests/datacite_dois/post_spec.rb +++ b/spec/requests/datacite_dois/post_spec.rb @@ -2194,7 +2194,7 @@ it "updates the Doi" do get "/dois/#{doi.doi}", nil, headers - expect(json.dig("data", "attributes", "descriptions")).to eq([{"description"=>"Data from: A new malaria agent in African hominids.", "descriptionType"=>"TechnicalInfo"}]) + expect(json.dig("data", "attributes", "descriptions")).to eq([{ "description" => "Data from: A new malaria agent in African hominids.", "descriptionType" => "TechnicalInfo" }]) expect(json.dig("data", "attributes", "container")).to be_empty patch "/dois/#{doi.doi}", update_attributes, headers From 743ac08027265a7ee66b39a247d7803b896ac188 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 8 Dec 2025 13:23:10 -0500 Subject: [PATCH 55/79] Fix a test - description. --- spec/models/doi_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/doi_spec.rb b/spec/models/doi_spec.rb index 6cd31d49e..451a01efb 100644 --- a/spec/models/doi_spec.rb +++ b/spec/models/doi_spec.rb @@ -1430,7 +1430,7 @@ publisher: publisher, publication_year: publication_year, types: types, - descriptions: [{ "description" => description }], + descriptions: [{ "description" => description, "descriptionType" => "Abstract" }], event: "publish") end From 235c3f9ff2330bb6db8e9610fecaa205ff4bd18e Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 8 Dec 2025 14:36:57 -0500 Subject: [PATCH 56/79] Fixes - code and test. --- app/models/doi.rb | 2 +- app/models/schemas/client/subjects.json | 2 +- app/models/schemas/doi/date.json | 23 +++++++ app/models/schemas/doi/dates.json | 20 +----- app/models/schemas/doi/related_items.json | 81 +---------------------- app/models/schemas/doi/subjects.json | 1 - spec/concerns/indexable_spec.rb | 2 +- 7 files changed, 29 insertions(+), 102 deletions(-) create mode 100644 app/models/schemas/doi/date.json diff --git a/app/models/doi.rb b/app/models/doi.rb index 34157e4f1..462df366b 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -159,7 +159,7 @@ def schema_file_path(schema_name) validates :subjects, if: proc { |doi| doi.validate_json_attribute?(:subjects) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("subjects") } }, unless: :only_validate validates :contributors, if: proc { |doi| doi.validate_json_attribute?(:contributors) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("contributors") } }, unless: :only_validate validates :dates, if: proc { |doi| doi.validate_json_attribute?(:dates) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("dates") } }, unless: :only_validate - # validates :language, if: proc { |doi| doi.validate_json_attribute?(:language) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("language") } }, unless: :only_validate + validates :language, if: proc { |doi| doi.validate_json_attribute?(:language) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("language") } }, unless: :only_validate validates :resource_type, if: proc { |doi| doi.validate_json_attribute?(:resource_type) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("resource_type") } }, unless: :only_validate validates :alternate_identifiers, if: proc { |doi| doi.validate_json_attribute?(:alternate_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("alternate_identifiers") } }, unless: :only_validate validates :related_identifiers, if: proc { |doi| doi.validate_json_attribute?(:related_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_identifiers") } }, unless: :only_validate diff --git a/app/models/schemas/client/subjects.json b/app/models/schemas/client/subjects.json index 69f9da678..bf4302384 100644 --- a/app/models/schemas/client/subjects.json +++ b/app/models/schemas/client/subjects.json @@ -14,7 +14,7 @@ ] }, "lang": { - "$ref": "language.json" + "$ref": "../doi/language.json" }, "subject": { "type": "string" }, "subjectScheme": { "type": "string" } diff --git a/app/models/schemas/doi/date.json b/app/models/schemas/doi/date.json new file mode 100644 index 000000000..e4319230b --- /dev/null +++ b/app/models/schemas/doi/date.json @@ -0,0 +1,23 @@ +{ + "title": "Date", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "date": { + "type": "string" + }, + "dateType": { + "$ref": "controlled_vocabularies/date_type.json" + }, + "dateInformation": { + "type": "string" + } + }, + "required": [ + "date" + ], + "dependentRequired": { + "date": ["dateType"] + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/app/models/schemas/doi/dates.json b/app/models/schemas/doi/dates.json index 294ce45fb..6f131ae46 100644 --- a/app/models/schemas/doi/dates.json +++ b/app/models/schemas/doi/dates.json @@ -3,24 +3,6 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "array", "items": { - "type": "object", - "properties": { - "date": { - "type": "string" - }, - "dateType": { - "$ref": "controlled_vocabularies/date_type.json" - }, - "dateInformation": { - "type": "string" - } - }, - "required": [ - "date" - ], - "dependentRequired": { - "date": ["dateType"] - }, - "additionalProperties": false + "$ref": "date.json" } } \ No newline at end of file diff --git a/app/models/schemas/doi/related_items.json b/app/models/schemas/doi/related_items.json index 7f0931582..780a30ffb 100644 --- a/app/models/schemas/doi/related_items.json +++ b/app/models/schemas/doi/related_items.json @@ -2,85 +2,8 @@ "title": "RelatedItems", "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "array", + "minItems": 0, "items": { - "type": "object", - "properties": { - "relatedItemType": { - "$ref": "controlled_vocabularies/related_item_type.json" - }, - "relationType": { - "$ref": "controlled_vocabularies/relation_type.json" - }, - "relatedItemIdentifier": { - "type": "object", - "properties": { - "relatedItemIdentifier": { - "type": "string" - }, - "relatedItemIdentifierType": { - "$ref": "controlled_vocabularies/related_identifier_type.json" - }, - "relatedMetadataScheme": { - "type": "string" - }, - "schemeURI": { - "type": "string" - }, - "schemeType": { - "type": "string" - } - }, - "additionalProperties": false - }, - "creators": { - "type": "array", - "minItems": 0, - "items": { - "$ref": "creator.json" - } - }, - "titles": { - "type": "array", - "minItems": 0, - "items": { - "$ref": "title.json" - } - }, - "publicationYear": { - "$ref": "publication_year.json" - }, - "volume": { - "type": "string" - }, - "issue": { - "type": "string" - }, - "number": { - "type": "string" - }, - "numberType": { - "$ref": "controlled_vocabularies/number_type.json" - }, - "firstPage": { - "type": "string" - }, - "lastPage": { - "type": "string" - }, - "publisher": { - "type": "string" - }, - "edition": { - "type": "string" - }, - "contributors": { - "type": "array", - "minItems": 0, - "items": { - "$ref": "contributor.json" - } - } - }, - "additionalProperties": true + "$ref": "relatedItem.json" } } \ No newline at end of file diff --git a/app/models/schemas/doi/subjects.json b/app/models/schemas/doi/subjects.json index 56b9add2e..c87f6cd40 100644 --- a/app/models/schemas/doi/subjects.json +++ b/app/models/schemas/doi/subjects.json @@ -2,7 +2,6 @@ "title": "Subjects", "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "array", - "minItems": 0, "items": { "$ref": "subject.json" } diff --git a/spec/concerns/indexable_spec.rb b/spec/concerns/indexable_spec.rb index 640ff8318..69e77cee2 100644 --- a/spec/concerns/indexable_spec.rb +++ b/spec/concerns/indexable_spec.rb @@ -71,7 +71,7 @@ :doi, titles: [ { title: "Soil investigations" } ], publisher: "Pangaea", - descriptions: [{ description: "this is a description" }], + descriptions: [{ description: "this is a description", descriptionType: "SeriesInformation" }], aasm_state: "findable", ) end From fae0e97699e97fceb03f1b1029ef3e39484cafc2 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 8 Dec 2025 14:50:21 -0500 Subject: [PATCH 57/79] Fix errors. --- app/models/schemas/doi/name_identifier.json | 2 +- app/models/schemas/doi/related_items.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/schemas/doi/name_identifier.json b/app/models/schemas/doi/name_identifier.json index 7ed3d8aa1..95c6e48e0 100644 --- a/app/models/schemas/doi/name_identifier.json +++ b/app/models/schemas/doi/name_identifier.json @@ -1,5 +1,5 @@ { - "title": "NameIdentifiers", + "title": "NameIdentifier", "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { diff --git a/app/models/schemas/doi/related_items.json b/app/models/schemas/doi/related_items.json index 780a30ffb..882b77ab0 100644 --- a/app/models/schemas/doi/related_items.json +++ b/app/models/schemas/doi/related_items.json @@ -4,6 +4,6 @@ "type": "array", "minItems": 0, "items": { - "$ref": "relatedItem.json" + "$ref": "related_item.json" } } \ No newline at end of file From d430aee3e3b97df43ab07c1aee918add521abbba Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 8 Dec 2025 15:35:19 -0500 Subject: [PATCH 58/79] Temporarily commenting out some tests having to do with language. --- spec/graphql/types/dissertation_type_spec.rb | 39 +++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/spec/graphql/types/dissertation_type_spec.rb b/spec/graphql/types/dissertation_type_spec.rb index 1a908f975..c0b11f596 100644 --- a/spec/graphql/types/dissertation_type_spec.rb +++ b/spec/graphql/types/dissertation_type_spec.rb @@ -11,12 +11,13 @@ end describe "query dissertations", elasticsearch: true do + # Commented out Language because there is an error on create. let!(:datacite_dissertations) do create_list( :doi, 2, types: { "resourceTypeGeneral" => "Text", "resourceType" => "Thesis" }, - language: "de", + # language: "de", aasm_state: "findable", ) end @@ -168,12 +169,13 @@ end describe "query dissertations by license", elasticsearch: true do + # Commented out Language because there is an error on create. let!(:datacite_dissertations) do create_list( :doi, 2, types: { "resourceTypeGeneral" => "Text", "resourceType" => "Thesis" }, - language: "de", + # language: "de", aasm_state: "findable", ) end @@ -225,22 +227,23 @@ }" end - it "returns all dissertations" do - response = LupoSchema.execute(query).as_json - - expect(response.dig("data", "dissertations", "totalCount")).to eq(2) - expect( - response.dig("data", "dissertations", "registrationAgencies"), - ).to eq([{ "count" => 2, "id" => "datacite", "title" => "DataCite" }]) - expect(response.dig("data", "dissertations", "languages")).to eq( - [{ "count" => 2, "id" => "de", "title" => "German" }], - ) - expect(response.dig("data", "dissertations", "nodes").length).to eq(2) - # expect(response.dig("data", "dissertations", "nodes", 0, "id")).to eq(@dois.first.identifier) - expect( - response.dig("data", "dissertations", "nodes", 0, "registrationAgency"), - ).to eq("id" => "datacite", "name" => "DataCite") - end + # Temporarily Commenting this out - Due to language validation problem? + # it "returns all dissertations" do + # response = LupoSchema.execute(query).as_json + # + # expect(response.dig("data", "dissertations", "totalCount")).to eq(2) + # expect( + # response.dig("data", "dissertations", "registrationAgencies"), + # ).to eq([{ "count" => 2, "id" => "datacite", "title" => "DataCite" }]) + # expect(response.dig("data", "dissertations", "languages")).to eq( + # [{ "count" => 2, "id" => "de", "title" => "German" }], + # ) + # expect(response.dig("data", "dissertations", "nodes").length).to eq(2) + # # expect(response.dig("data", "dissertations", "nodes", 0, "id")).to eq(@dois.first.identifier) + # expect( + # response.dig("data", "dissertations", "nodes", 0, "registrationAgency"), + # ).to eq("id" => "datacite", "name" => "DataCite") + # end end describe "query dissertations by person", elasticsearch: true do From 05b301e5d38aad88623e36d3b10f930d0d630c41 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 8 Dec 2025 16:13:48 -0500 Subject: [PATCH 59/79] Comment out language validation. There is a problem with it. --- app/models/doi.rb | 2 +- spec/graphql/types/dissertation_type_spec.rb | 39 +++++++++----------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/app/models/doi.rb b/app/models/doi.rb index 462df366b..34157e4f1 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -159,7 +159,7 @@ def schema_file_path(schema_name) validates :subjects, if: proc { |doi| doi.validate_json_attribute?(:subjects) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("subjects") } }, unless: :only_validate validates :contributors, if: proc { |doi| doi.validate_json_attribute?(:contributors) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("contributors") } }, unless: :only_validate validates :dates, if: proc { |doi| doi.validate_json_attribute?(:dates) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("dates") } }, unless: :only_validate - validates :language, if: proc { |doi| doi.validate_json_attribute?(:language) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("language") } }, unless: :only_validate + # validates :language, if: proc { |doi| doi.validate_json_attribute?(:language) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("language") } }, unless: :only_validate validates :resource_type, if: proc { |doi| doi.validate_json_attribute?(:resource_type) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("resource_type") } }, unless: :only_validate validates :alternate_identifiers, if: proc { |doi| doi.validate_json_attribute?(:alternate_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("alternate_identifiers") } }, unless: :only_validate validates :related_identifiers, if: proc { |doi| doi.validate_json_attribute?(:related_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_identifiers") } }, unless: :only_validate diff --git a/spec/graphql/types/dissertation_type_spec.rb b/spec/graphql/types/dissertation_type_spec.rb index c0b11f596..b551e8bec 100644 --- a/spec/graphql/types/dissertation_type_spec.rb +++ b/spec/graphql/types/dissertation_type_spec.rb @@ -11,13 +11,12 @@ end describe "query dissertations", elasticsearch: true do - # Commented out Language because there is an error on create. let!(:datacite_dissertations) do create_list( :doi, 2, types: { "resourceTypeGeneral" => "Text", "resourceType" => "Thesis" }, - # language: "de", + language: "de", aasm_state: "findable", ) end @@ -169,13 +168,12 @@ end describe "query dissertations by license", elasticsearch: true do - # Commented out Language because there is an error on create. let!(:datacite_dissertations) do create_list( :doi, 2, types: { "resourceTypeGeneral" => "Text", "resourceType" => "Thesis" }, - # language: "de", + language: "de", aasm_state: "findable", ) end @@ -227,23 +225,22 @@ }" end - # Temporarily Commenting this out - Due to language validation problem? - # it "returns all dissertations" do - # response = LupoSchema.execute(query).as_json - # - # expect(response.dig("data", "dissertations", "totalCount")).to eq(2) - # expect( - # response.dig("data", "dissertations", "registrationAgencies"), - # ).to eq([{ "count" => 2, "id" => "datacite", "title" => "DataCite" }]) - # expect(response.dig("data", "dissertations", "languages")).to eq( - # [{ "count" => 2, "id" => "de", "title" => "German" }], - # ) - # expect(response.dig("data", "dissertations", "nodes").length).to eq(2) - # # expect(response.dig("data", "dissertations", "nodes", 0, "id")).to eq(@dois.first.identifier) - # expect( - # response.dig("data", "dissertations", "nodes", 0, "registrationAgency"), - # ).to eq("id" => "datacite", "name" => "DataCite") - # end + it "returns all dissertations" do + response = LupoSchema.execute(query).as_json + + expect(response.dig("data", "dissertations", "totalCount")).to eq(2) + expect( + response.dig("data", "dissertations", "registrationAgencies"), + ).to eq([{ "count" => 2, "id" => "datacite", "title" => "DataCite" }]) + expect(response.dig("data", "dissertations", "languages")).to eq( + [{ "count" => 2, "id" => "de", "title" => "German" }], + ) + expect(response.dig("data", "dissertations", "nodes").length).to eq(2) + # expect(response.dig("data", "dissertations", "nodes", 0, "id")).to eq(@dois.first.identifier) + expect( + response.dig("data", "dissertations", "nodes", 0, "registrationAgency"), + ).to eq("id" => "datacite", "name" => "DataCite") + end end describe "query dissertations by person", elasticsearch: true do From 7a9609076b972b87c02d66dd3aa0a1af343f8d99 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 8 Dec 2025 16:16:24 -0500 Subject: [PATCH 60/79] Appease rubocop. --- spec/graphql/types/dissertation_type_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/graphql/types/dissertation_type_spec.rb b/spec/graphql/types/dissertation_type_spec.rb index b551e8bec..1a908f975 100644 --- a/spec/graphql/types/dissertation_type_spec.rb +++ b/spec/graphql/types/dissertation_type_spec.rb @@ -227,7 +227,7 @@ it "returns all dissertations" do response = LupoSchema.execute(query).as_json - + expect(response.dig("data", "dissertations", "totalCount")).to eq(2) expect( response.dig("data", "dissertations", "registrationAgencies"), From 2148812f52c260aaf0dbc3538da299f9cb3a6062 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 8 Dec 2025 18:00:13 -0500 Subject: [PATCH 61/79] Fix tests. --- app/models/doi.rb | 1 + spec/models/doi_spec.rb | 2 +- spec/requests/datacite_dois/post_geo_spec.rb | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/models/doi.rb b/app/models/doi.rb index 34157e4f1..5d1ced197 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -159,6 +159,7 @@ def schema_file_path(schema_name) validates :subjects, if: proc { |doi| doi.validate_json_attribute?(:subjects) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("subjects") } }, unless: :only_validate validates :contributors, if: proc { |doi| doi.validate_json_attribute?(:contributors) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("contributors") } }, unless: :only_validate validates :dates, if: proc { |doi| doi.validate_json_attribute?(:dates) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("dates") } }, unless: :only_validate + # temporarily commenting out because of problems with language validation. # validates :language, if: proc { |doi| doi.validate_json_attribute?(:language) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("language") } }, unless: :only_validate validates :resource_type, if: proc { |doi| doi.validate_json_attribute?(:resource_type) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("resource_type") } }, unless: :only_validate validates :alternate_identifiers, if: proc { |doi| doi.validate_json_attribute?(:alternate_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("alternate_identifiers") } }, unless: :only_validate diff --git a/spec/models/doi_spec.rb b/spec/models/doi_spec.rb index 451a01efb..da548f279 100644 --- a/spec/models/doi_spec.rb +++ b/spec/models/doi_spec.rb @@ -1477,7 +1477,7 @@ end it "descriptions" do - expect(subject.descriptions).to eq([{ "description" => description }]) + expect(subject.descriptions).to eq([{ "description" => description, "descriptionType" => "Abstract" }]) xml = Maremma.from_xml(subject.xml).fetch("resource", {}) expect(xml.dig("descriptions", "description")).to eq("__content__" => "Eating your own dog food is a slang term to describe that an organization should itself use the products and services it provides. For DataCite this means that we should use DOIs with appropriate metadata and strategies for long-term preservation for...", "descriptionType" => "Abstract") diff --git a/spec/requests/datacite_dois/post_geo_spec.rb b/spec/requests/datacite_dois/post_geo_spec.rb index 0d46ca5eb..a8d2302a9 100644 --- a/spec/requests/datacite_dois/post_geo_spec.rb +++ b/spec/requests/datacite_dois/post_geo_spec.rb @@ -132,7 +132,7 @@ { "lang" => "en", "description" => "Diet and physical activity are two modifiable factors that can curtail the development of osteoporosis in the aging population. One purpose of this study was to assess the differences in dietary intake and bone mineral density (BMD) in a Masters athlete population (n=87, n=49 female; 41.06 ± 5.00 years of age) and examine sex- and sport-related differences in dietary and total calcium and vitamin K intake and BMD of the total body, lumbar spine, and dual femoral neck (TBBMD, LSBMD and DFBMD, respectively). Total calcium is defined as calcium intake from diet and supplements. Athletes were categorized as participating in an endurance or interval sport. BMD was measured using dual-energy X-ray absorptiometry (DXA). Data on dietary intake was collected from Block 2005 Food Frequency Questionnaires (FFQs). Dietary calcium, total calcium, or vitamin K intake did not differ between the female endurance and interval athletes. All three BMD sites were significantly different among the female endurance and interval athletes, with female interval athletes having higher BMD at each site (TBBMD: 1.26 ± 0.10 g/cm2, p<0.05; LSBMD: 1.37 ± 0.14 g/cm2, p<0.01; DFBMD: 1.11 ± 0.12 g/cm2, p<0.05, for female interval athletes; TBBMD: 1.19 ± 0.09 g/cm2; LSBMD: 1.23 ± 0.16 g/cm2; DFBMD: 1.04 ± 0.10 g/cm2, for female endurance athletes). Male interval athletes had higher BMD at all three sites (TBBMD 1.44 ± 0.11 g/cm2, p<0.05; LSBMD 1.42 ± 0.15 g/cm2, p=0.179; DFBMD 1.26 ± 0.14 g/cm2, p<0.01, for male interval athletes; TBBMD 1.33 ± 0.11 g/cm2; LSBMD 1.33 ± 0.17 g/cm2; DFBMD 1.10 ± 0.12 g/cm2 for male endurance athletes). Dietary calcium, total daily calcium and vitamin K intake did not differ between the male endurance and interval athletes. This study evaluated the relationship between calcium intake and BMD. No relationship between dietary or total calcium intake and BMD was evident in all female athletes, female endurance athletes or female interval athletes. In all male athletes, there was no significant correlation between dietary or total calcium intake and BMD at any of the measured sites. However, the male interval athlete group had a negative relationship between dietary calcium intake and TBBMD (r=-0.738, p<0.05) and LSBMD (r=-0.738, p<0.05). The negative relationship persisted between total calcium intake and LSBMD (r=-0.714, p<0.05), but not TBBMD, when calcium from supplements was included. The third purpose of this study was to evaluate the relationship between vitamin K intake (as phylloquinone) and BMD. In all female athletes, there was no significant correlation between vitamin K intake and BMD at any of the measured sites. No relationship between vitamin K and BMD was evident in female interval or female endurance athletes. Similarly, there was no relationship between vitamin K intake and BMD in the male endurance and interval groups. The final purpose of this study was to assess the relationship between the Calcium-to-Vitamin K (Ca:K) ratio and BMD. A linear regression model established that the ratio predicted TBBMD in female athletes, F(1,47) = 4.652, p <0.05, and the ratio accounted for 9% of the variability in TBBMD. The regression equation was: predicted TBBMD in a female athlete = 1.250 - 0.008 x (Ca:K). In conclusion, Masters interval athletes have higher BMD than Masters endurance athletes; however, neither dietary or supplemental calcium nor vitamin K were related to BMD in skeletal sites prone to fracture in older adulthood. We found that a Ca:K ratio could predict TBBMD in female athletes. Further research should consider the calcium-to-vitamin K relationship in conjunction with other modifiable, lifestyle factors associated with bone health in the investigation of methods to minimize the development and effect of osteoporosis in the older athlete population.", - "descriptionType" => "Abstract", + "descriptionType" => "Abstract" }, ], "geoLocations" => [ From 8e953cda6d828930b73ca5e29309539774a10ffa Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 8 Dec 2025 23:47:42 -0500 Subject: [PATCH 62/79] Add fundingReference validation. --- app/models/doi.rb | 3 +- .../funder_identifier_type.json | 12 +++++++ app/models/schemas/doi/funding_reference.json | 35 +++++++++++++++++++ .../schemas/doi/funding_references.json | 9 +++++ 4 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 app/models/schemas/doi/controlled_vocabularies/funder_identifier_type.json create mode 100644 app/models/schemas/doi/funding_reference.json create mode 100644 app/models/schemas/doi/funding_references.json diff --git a/app/models/doi.rb b/app/models/doi.rb index 5d1ced197..933fd83fb 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -166,11 +166,12 @@ def schema_file_path(schema_name) validates :related_identifiers, if: proc { |doi| doi.validate_json_attribute?(:related_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_identifiers") } }, unless: :only_validate validates :sizes, if: proc { |doi| doi.validate_json_attribute?(:sizes) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("sizes") } }, unless: :only_validate validates :formats, if: proc { |doi| doi.validate_json_attribute?(:formats) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("formats") } }, unless: :only_validate + # temporarily commenting out because of problems with version validation. # validates :version, if: proc { |doi| doi.validate_json_attribute?(:version) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("version") } }, unless: :only_validate validates :rights_list, if: proc { |doi| doi.validate_json_attribute?(:rights_list) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("rights_list") } }, unless: :only_validate validates :descriptions, if: proc { |doi| doi.validate_json_attribute?(:descriptions) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("descriptions") } }, unless: :only_validate # validates :geolocations, if: proc { |doi| doi.validate_json_attribute?(:geolocations) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("geolocations") } }, unless: :only_validate - # validates :funding_references, if: proc { |doi| doi.validate_json_attribute?(:funding_references) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("funding_references") } }, unless: :only_validate + validates :funding_references, if: proc { |doi| doi.validate_json_attribute?(:funding_references) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("funding_references") } }, unless: :only_validate validates :related_items, if: proc { |doi| doi.validate_json_attribute?(:related_items) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_items") } }, unless: :only_validate after_commit :update_url, on: %i[create update] diff --git a/app/models/schemas/doi/controlled_vocabularies/funder_identifier_type.json b/app/models/schemas/doi/controlled_vocabularies/funder_identifier_type.json new file mode 100644 index 000000000..50c0b9972 --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/funder_identifier_type.json @@ -0,0 +1,12 @@ +{ + "title": "FunderIdentifierType", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string", + "enum": [ + "Crossref Funder ID", + "GRID", + "ISNI", + "ROR", + "Other" + ] +} \ No newline at end of file diff --git a/app/models/schemas/doi/funding_reference.json b/app/models/schemas/doi/funding_reference.json new file mode 100644 index 000000000..f76fe4b76 --- /dev/null +++ b/app/models/schemas/doi/funding_reference.json @@ -0,0 +1,35 @@ +{ + "title": "FundingReference", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "funderName": { + "type": ["string", "null"] + }, + "funderIdentifier": { + "type": ["string", "null"] + }, + "funderIdentifierType": { + "$ref": "controlled_vocabularies/funder_identifier_type.json" + }, + "awardNumber": { + "type": ["string", "null"] + }, + "awardUri": { + "type": ["string", "null"] + }, + "awardTitle": { + "type": ["string", "null"] + }, + "schemeUri": { + "type": ["string", "null"] + } + }, + "required": [ + "funderName" + ], + "dependentRequired": { + "funderIdentifier": ["funderIdentifierType"] + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/app/models/schemas/doi/funding_references.json b/app/models/schemas/doi/funding_references.json new file mode 100644 index 000000000..086176618 --- /dev/null +++ b/app/models/schemas/doi/funding_references.json @@ -0,0 +1,9 @@ +{ + "title": "FundingReferences", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "minItems": 0, + "items": { + "$ref": "funding_reference.json" + } +} \ No newline at end of file From 091b665304b97ff40ba77371299eaf10ea5b557f Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Tue, 9 Dec 2025 00:11:55 -0500 Subject: [PATCH 63/79] Fix test. --- spec/models/doi_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/doi_spec.rb b/spec/models/doi_spec.rb index da548f279..fa52448ca 100644 --- a/spec/models/doi_spec.rb +++ b/spec/models/doi_spec.rb @@ -63,7 +63,7 @@ travel_to(Time.zone.local(2023, 12, 14, 10, 7, 40)) do expect(doi).to receive(:send_import_message).with(doi.to_jsonapi) - doi.update(funding_references: [{ "funder" => "New Funder", "title" => "New Title" }]) + doi.update(funding_references: [{ "funderName" => "New Funder", "awardTitle" => "New Award Title" }]) end end From f27dcee4fb5e6b8847394e7e9f2fadbde3697bf2 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Tue, 9 Dec 2025 01:11:47 -0500 Subject: [PATCH 64/79] geoLocation validation. --- app/models/schemas/doi/geo_location.json | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 app/models/schemas/doi/geo_location.json diff --git a/app/models/schemas/doi/geo_location.json b/app/models/schemas/doi/geo_location.json new file mode 100644 index 000000000..703dfa560 --- /dev/null +++ b/app/models/schemas/doi/geo_location.json @@ -0,0 +1,32 @@ +{ + "title": "GeoLocation", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "anyOf": [ + { + "geoLocationPlace": { "type": "string" } + }, + { + "geoLocationPlace": { + "type": "object", + "properties": { + "geoLocationpoint": { + "type": "object", + "properties" { + "pointLatitude": { + "type": [ "number", "string"] + }, + "pointLongitude": { + "type": [ "number", "string"] + } + }, + "required": [ + "pointLatitude", + "pointLongitude" + ] + } + } + } + } + ] +} \ No newline at end of file From fe157785aa0f7a743ac0a95c4de7e0435e674ebb Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Wed, 10 Dec 2025 12:46:19 -0500 Subject: [PATCH 65/79] Add json schema validation for geoLocations. --- app/models/doi.rb | 2 +- app/models/schemas/doi/geo_location.json | 42 ++++++++----------- app/models/schemas/doi/geo_location_box.json | 26 ++++++++++++ .../schemas/doi/geo_location_place.json | 5 +++ .../schemas/doi/geo_location_point.json | 18 ++++++++ .../schemas/doi/geo_location_polygon.json | 23 ++++++++++ app/models/schemas/doi/geo_locations.json | 8 ++++ app/models/schemas/doi/in_polygon_point.json | 18 ++++++++ app/models/schemas/doi/point_latitude.json | 5 +++ app/models/schemas/doi/point_longitude.json | 5 +++ app/models/schemas/doi/polygon_point.json | 18 ++++++++ 11 files changed, 144 insertions(+), 26 deletions(-) create mode 100644 app/models/schemas/doi/geo_location_box.json create mode 100644 app/models/schemas/doi/geo_location_place.json create mode 100644 app/models/schemas/doi/geo_location_point.json create mode 100644 app/models/schemas/doi/geo_location_polygon.json create mode 100644 app/models/schemas/doi/geo_locations.json create mode 100644 app/models/schemas/doi/in_polygon_point.json create mode 100644 app/models/schemas/doi/point_latitude.json create mode 100644 app/models/schemas/doi/point_longitude.json create mode 100644 app/models/schemas/doi/polygon_point.json diff --git a/app/models/doi.rb b/app/models/doi.rb index 933fd83fb..b5e3daaec 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -170,7 +170,7 @@ def schema_file_path(schema_name) # validates :version, if: proc { |doi| doi.validate_json_attribute?(:version) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("version") } }, unless: :only_validate validates :rights_list, if: proc { |doi| doi.validate_json_attribute?(:rights_list) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("rights_list") } }, unless: :only_validate validates :descriptions, if: proc { |doi| doi.validate_json_attribute?(:descriptions) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("descriptions") } }, unless: :only_validate - # validates :geolocations, if: proc { |doi| doi.validate_json_attribute?(:geolocations) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("geolocations") } }, unless: :only_validate + validates :geolocations, if: proc { |doi| doi.validate_json_attribute?(:geolocations) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("geolocations") } }, unless: :only_validate validates :funding_references, if: proc { |doi| doi.validate_json_attribute?(:funding_references) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("funding_references") } }, unless: :only_validate validates :related_items, if: proc { |doi| doi.validate_json_attribute?(:related_items) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_items") } }, unless: :only_validate diff --git a/app/models/schemas/doi/geo_location.json b/app/models/schemas/doi/geo_location.json index 703dfa560..58d2c2f11 100644 --- a/app/models/schemas/doi/geo_location.json +++ b/app/models/schemas/doi/geo_location.json @@ -2,31 +2,23 @@ "title": "GeoLocation", "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", - "anyOf": [ - { - "geoLocationPlace": { "type": "string" } + "properties": { + "geoLocationPoint": { + "$ref": "geo_location_point.json" }, - { - "geoLocationPlace": { - "type": "object", - "properties": { - "geoLocationpoint": { - "type": "object", - "properties" { - "pointLatitude": { - "type": [ "number", "string"] - }, - "pointLongitude": { - "type": [ "number", "string"] - } - }, - "required": [ - "pointLatitude", - "pointLongitude" - ] - } - } - } + "geoLocationBox": { + "$ref": "geo_location_box.json" + }, + "geoLocationPlace": { + "$ref": "geo_location_place.json" + }, + "geoLocationPolygon": { + "$ref": "geo_location_polygon.json" } - ] + }, + "required": [ + "pointLatitude", + "pointLongitude" + ], + "additionalProperties": false } \ No newline at end of file diff --git a/app/models/schemas/doi/geo_location_box.json b/app/models/schemas/doi/geo_location_box.json new file mode 100644 index 000000000..a9916ab34 --- /dev/null +++ b/app/models/schemas/doi/geo_location_box.json @@ -0,0 +1,26 @@ +{ + "title": "GeoLocationBox", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "eastBoundLongitude": { + "$ref": "point_longitude.json" + }, + "northBoundLatitude": { + "$ref": "point_latitude.json" + }, + "southBoundLatitude": { + "$ref": "point_latitude.json" + }, + "westBoundLongitude": { + "$ref": "point_longitude.json" + } + }, + "required": [ + "eastBoundLongitude", + "northBoundLatitude", + "southBoundLatitude", + "westBoundLongitude" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/app/models/schemas/doi/geo_location_place.json b/app/models/schemas/doi/geo_location_place.json new file mode 100644 index 000000000..0c1dc642e --- /dev/null +++ b/app/models/schemas/doi/geo_location_place.json @@ -0,0 +1,5 @@ +{ + "title": "GeoLocationSize", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string" +} \ No newline at end of file diff --git a/app/models/schemas/doi/geo_location_point.json b/app/models/schemas/doi/geo_location_point.json new file mode 100644 index 000000000..8fccb6fdf --- /dev/null +++ b/app/models/schemas/doi/geo_location_point.json @@ -0,0 +1,18 @@ +{ + "title": "GeoLocationPoint", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "pointLatitude": { + "$ref": "point_latitude.json" + }, + "pointLongitude": { + "$ref": "point_longitude.json" + } + }, + "required": [ + "pointLatitude", + "pointLongitude" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/app/models/schemas/doi/geo_location_polygon.json b/app/models/schemas/doi/geo_location_polygon.json new file mode 100644 index 000000000..bf5387faf --- /dev/null +++ b/app/models/schemas/doi/geo_location_polygon.json @@ -0,0 +1,23 @@ +{ + "title": "GeoLocationPolygon", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "minItems": 4, + "items": { + "oneOf": [ + { "$ref": "polygon_point" }, + { "$ref": "in_polygon_point" } + ] + }, + "allOf": [ + { + "contains": { "$ref": "polygon_point" }, + "minContains": 4 + }, + { + "contains": { "$ref": "in_polygon_point" }, + "minContains": 0, + "maxContains": 1 + } + ] +} \ No newline at end of file diff --git a/app/models/schemas/doi/geo_locations.json b/app/models/schemas/doi/geo_locations.json new file mode 100644 index 000000000..71b98e123 --- /dev/null +++ b/app/models/schemas/doi/geo_locations.json @@ -0,0 +1,8 @@ +{ + "title": "GeoLocations", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "items": { + "$ref": "geo_location.json" + } +} diff --git a/app/models/schemas/doi/in_polygon_point.json b/app/models/schemas/doi/in_polygon_point.json new file mode 100644 index 000000000..cad9831e1 --- /dev/null +++ b/app/models/schemas/doi/in_polygon_point.json @@ -0,0 +1,18 @@ +{ + "title": "InPolygonPoint", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "pointLatitude": { + "$ref": "point_latitude.json" + }, + "pointLongitude": { + "$ref": "point_longitude.json" + } + }, + "required": [ + "pointLatitude", + "pointLongitude" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/app/models/schemas/doi/point_latitude.json b/app/models/schemas/doi/point_latitude.json new file mode 100644 index 000000000..54fdedd14 --- /dev/null +++ b/app/models/schemas/doi/point_latitude.json @@ -0,0 +1,5 @@ +{ + "title": "PointLatitude", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": [ "number", "string" ] +} \ No newline at end of file diff --git a/app/models/schemas/doi/point_longitude.json b/app/models/schemas/doi/point_longitude.json new file mode 100644 index 000000000..6040d9d3c --- /dev/null +++ b/app/models/schemas/doi/point_longitude.json @@ -0,0 +1,5 @@ +{ + "title": "PointLongitude", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": [ "number", "string" ] +} \ No newline at end of file diff --git a/app/models/schemas/doi/polygon_point.json b/app/models/schemas/doi/polygon_point.json new file mode 100644 index 000000000..c0cf7865e --- /dev/null +++ b/app/models/schemas/doi/polygon_point.json @@ -0,0 +1,18 @@ +{ + "title": "PolygonPoint", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "pointLatitude": { + "$ref": "point_latitude.json" + }, + "pointLongitude": { + "$ref": "point_longitude.json" + } + }, + "required": [ + "pointLatitude", + "pointLongitude" + ], + "additionalProperties": false +} \ No newline at end of file From 5e831a9300fb5f1514e74a4088f9aafbe4bf942d Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Wed, 10 Dec 2025 13:40:40 -0500 Subject: [PATCH 66/79] Fix --- app/models/schemas/doi/geo_location_place.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/schemas/doi/geo_location_place.json b/app/models/schemas/doi/geo_location_place.json index 0c1dc642e..f5b6e5050 100644 --- a/app/models/schemas/doi/geo_location_place.json +++ b/app/models/schemas/doi/geo_location_place.json @@ -1,5 +1,5 @@ { - "title": "GeoLocationSize", + "title": "GeoLocationPlace", "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "string" } \ No newline at end of file From d719846d2ed0ccc1437ae648902800c79c747168 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Wed, 10 Dec 2025 23:27:33 -0500 Subject: [PATCH 67/79] Fixes. --- .../related_item_identifier_type.json | 28 ------- app/models/schemas/doi/related_item.json | 74 +++++++++++++------ .../schemas/doi/related_item_identifier.json | 23 ++++++ 3 files changed, 74 insertions(+), 51 deletions(-) delete mode 100644 app/models/schemas/doi/controlled_vocabularies/related_item_identifier_type.json create mode 100644 app/models/schemas/doi/related_item_identifier.json diff --git a/app/models/schemas/doi/controlled_vocabularies/related_item_identifier_type.json b/app/models/schemas/doi/controlled_vocabularies/related_item_identifier_type.json deleted file mode 100644 index 716a0f961..000000000 --- a/app/models/schemas/doi/controlled_vocabularies/related_item_identifier_type.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "title": "RelatedItemIdentifierType", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "string", - "enum": [ - "ARK", - "arXiv", - "bibcode", - "CSTR", - "DOI", - "EAN13", - "EISSN", - "Handle", - "IGSN", - "ISBN", - "ISSN", - "ISTC", - "LISSN", - "LSID", - "PMID", - "PURL", - "RRID", - "UPC", - "URL", - "URN", - "w3id" - ] -} \ No newline at end of file diff --git a/app/models/schemas/doi/related_item.json b/app/models/schemas/doi/related_item.json index b2febd3f6..4da617c07 100644 --- a/app/models/schemas/doi/related_item.json +++ b/app/models/schemas/doi/related_item.json @@ -4,32 +4,11 @@ "type": "object", "properties": { "relatedItemType": { - "$ref": "controlled_vocabularies/related_item_type.json" + "$ref": "controlled_vocabularies/resource_type_general.json" }, "relationType": { "$ref": "controlled_vocabularies/relation_type.json" }, - "relatedItemIdentifier": { - "type": "object", - "properties": { - "relatedItemIdentifier": { - "type": "string" - }, - "relatedItemIdentifierType": { - "$ref": "controlled_vocabularies/related_identifier_type.json" - }, - "relatedMetadataScheme": { - "type": "string" - }, - "schemeURI": { - "type": "string" - }, - "schemeType": { - "type": "string" - } - }, - "additionalProperties": false - }, "creators": { "type": "array", "minItems": 0, @@ -79,5 +58,54 @@ } } }, - "additionalProperties": true + "if": { + "properties": { + "relationType": { + "enum": [ "HasMetadata", "IsMetadataFor" ] + } + } + }, + "then": { + "properties": { + "relatedItemIdentifier": { + "type": "object", + "properties": { + "relatedItemIdentifier": { + "type": "string" + }, + "relatedItemIdentifierType": { + "$ref": "controlled_vocabularies/related_identifier_type.json" + }, + "relatedMetadataScheme": { + "type": "string" + }, + "schemeURI": { + "type": "string" + }, + "schemeType": { + "type": "string" + } + } + } + } + }, + "else": { + "properties": { + "relatedItemIdentifier": { + "type": "object", + "properties": { + "relatedItemIdentifier": { + "type": "string" + }, + "relatedItemIdentifierType": { + "$ref": "controlled_vocabularies/related_identifier_type.json" + } + } + } + } + }, + "required": [ + "relatedItemType", + "relationType" + ] } \ No newline at end of file diff --git a/app/models/schemas/doi/related_item_identifier.json b/app/models/schemas/doi/related_item_identifier.json new file mode 100644 index 000000000..5de94221f --- /dev/null +++ b/app/models/schemas/doi/related_item_identifier.json @@ -0,0 +1,23 @@ +{ + "title": "RelatedItemIdentifier", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "relatedItemIdentifier": { + "type": "string" + }, + "relatedItemIdentifierType": { + "$ref": "controlled_vocabularies/related_identifier_type.json" + }, + "relatedMetadataScheme": { + "type": "string" + }, + "schemeURI": { + "type": "string" + }, + "schemeType": { + "type": "string" + } + }, + "additionalProperties": false +} \ No newline at end of file From e94504039b3a2179ad17fcf9a49823b33886d64c Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Thu, 11 Dec 2025 02:02:27 -0500 Subject: [PATCH 68/79] Fix errors. --- app/models/schemas/doi/related_item.json | 10 ++++++++-- spec/requests/datacite_dois/post_spec.rb | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/models/schemas/doi/related_item.json b/app/models/schemas/doi/related_item.json index 4da617c07..27e0cf4e3 100644 --- a/app/models/schemas/doi/related_item.json +++ b/app/models/schemas/doi/related_item.json @@ -9,6 +9,9 @@ "relationType": { "$ref": "controlled_vocabularies/relation_type.json" }, + "relatedItemIdentifier": { + "type": "object" + }, "creators": { "type": "array", "minItems": 0, @@ -85,7 +88,8 @@ "schemeType": { "type": "string" } - } + }, + "additionalProperties": false } } }, @@ -100,10 +104,12 @@ "relatedItemIdentifierType": { "$ref": "controlled_vocabularies/related_identifier_type.json" } - } + }, + "additionalProperties": false } } }, + "unevaluatedProperties": false, "required": [ "relatedItemType", "relationType" diff --git a/spec/requests/datacite_dois/post_spec.rb b/spec/requests/datacite_dois/post_spec.rb index 5f2666fb3..048f5d1e5 100644 --- a/spec/requests/datacite_dois/post_spec.rb +++ b/spec/requests/datacite_dois/post_spec.rb @@ -357,7 +357,7 @@ "schemeType" => "URL" }, "relatedItemType" => "Journal", - "relationType" => "IsPublishedIn", + "relationType" => "HasMetadata", "titles" => [{ "title" => "Physics letters / B" }], "volume" => "776" }], @@ -388,7 +388,7 @@ expect(json.dig("data", "attributes", "source")).to eq("test") expect(json.dig("data", "attributes", "types")).to eq("bibtex" => "article", "citeproc" => "article-journal", "resourceType" => "BlogPosting", "resourceTypeGeneral" => "Text", "ris" => "RPRT", "schemaOrg" => "ScholarlyArticle") expect(json.dig("data", "attributes", "state")).to eq("findable") - expect(json.dig("data", "attributes", "relatedItems")).to eq(["relationType" => "IsPublishedIn", + expect(json.dig("data", "attributes", "relatedItems")).to eq(["relationType" => "HasMetadata", "relatedItemType" => "Journal", "publicationYear" => "2018", "relatedItemIdentifier" => { From f428d24173c46e87e6bbdf7964cfb15e2bddb85d Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Thu, 11 Dec 2025 02:22:33 -0500 Subject: [PATCH 69/79] Fixes. --- app/models/doi.rb | 3 +-- app/models/schemas/doi/version.json | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/models/doi.rb b/app/models/doi.rb index 10a0f39bf..80953c354 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -166,8 +166,7 @@ def schema_file_path(schema_name) validates :related_identifiers, if: proc { |doi| doi.validate_json_attribute?(:related_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_identifiers") } }, unless: :only_validate validates :sizes, if: proc { |doi| doi.validate_json_attribute?(:sizes) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("sizes") } }, unless: :only_validate validates :formats, if: proc { |doi| doi.validate_json_attribute?(:formats) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("formats") } }, unless: :only_validate - # temporarily commenting out because of problems with version validation. - # validates :version, if: proc { |doi| doi.validate_json_attribute?(:version) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("version") } }, unless: :only_validate + validates :version, if: proc { |doi| doi.validate_json_attribute?(:version) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("version") } }, unless: :only_validate validates :rights_list, if: proc { |doi| doi.validate_json_attribute?(:rights_list) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("rights_list") } }, unless: :only_validate validates :descriptions, if: proc { |doi| doi.validate_json_attribute?(:descriptions) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("descriptions") } }, unless: :only_validate validates :geolocations, if: proc { |doi| doi.validate_json_attribute?(:geolocations) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("geolocations") } }, unless: :only_validate diff --git a/app/models/schemas/doi/version.json b/app/models/schemas/doi/version.json index 93529b692..aebd57662 100644 --- a/app/models/schemas/doi/version.json +++ b/app/models/schemas/doi/version.json @@ -1,5 +1,5 @@ { "title": "Version", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "string" + "type": [ "number", "string" ] } From bb141dc52e8e506fa4f6a4d7635bc05f26ae4bda Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 15 Dec 2025 22:21:05 -0500 Subject: [PATCH 70/79] fixes --- app/models/doi.rb | 9 +++++++-- app/models/schemas/client/subjects.json | 2 +- app/models/schemas/doi/description.json | 4 ++-- app/models/schemas/doi/identifier.json | 3 +-- app/models/schemas/doi/language.json | 4 ++-- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/app/models/doi.rb b/app/models/doi.rb index 80953c354..742c03465 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -151,7 +151,8 @@ def schema_file_path(schema_name) validate :check_language, if: :language? # JSON-SCHEMA VALIDATION - # validates :identifier, if: proc { |doi| doi.validate_json_attribute?(:identifier) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("identifier") } }, unless: :only_validate + # temporarily commenting out this validation. + # validates :doi, if: proc { |doi| doi.validate_json_attribute?(:identifier) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("identifier") } }, unless: :only_validate validates :creators, if: proc { |doi| doi.validate_json_attribute?(:creators) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("creators") } }, unless: :only_validate validates :titles, if: proc { |doi| doi.validate_json_attribute?(:titles) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("titles") } }, unless: :only_validate validates :publisher_obj, if: proc { |doi| doi.validate_json_attribute?(:publisher_obj) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("publisher") } }, unless: :only_validate @@ -160,7 +161,7 @@ def schema_file_path(schema_name) validates :contributors, if: proc { |doi| doi.validate_json_attribute?(:contributors) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("contributors") } }, unless: :only_validate validates :dates, if: proc { |doi| doi.validate_json_attribute?(:dates) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("dates") } }, unless: :only_validate # temporarily commenting out because of problems with language validation. - # validates :language, if: proc { |doi| doi.validate_json_attribute?(:language) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("language") } }, unless: :only_validate + validates :language, if: proc { |doi| doi.validate_json_attribute?(:language) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("language") } }, unless: :only_validate validates :resource_type, if: proc { |doi| doi.validate_json_attribute?(:resource_type) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("resource_type") } }, unless: :only_validate validates :alternate_identifiers, if: proc { |doi| doi.validate_json_attribute?(:alternate_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("alternate_identifiers") } }, unless: :only_validate validates :related_identifiers, if: proc { |doi| doi.validate_json_attribute?(:related_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_identifiers") } }, unless: :only_validate @@ -1772,6 +1773,10 @@ def sizes=(value) def dates=(value) write_attribute(:dates, Array.wrap(value)) end + + def language=(value) + write_attribute(:language, value) + end def subjects=(value) write_attribute(:subjects, Array.wrap(value)) diff --git a/app/models/schemas/client/subjects.json b/app/models/schemas/client/subjects.json index bf4302384..69f9da678 100644 --- a/app/models/schemas/client/subjects.json +++ b/app/models/schemas/client/subjects.json @@ -14,7 +14,7 @@ ] }, "lang": { - "$ref": "../doi/language.json" + "$ref": "language.json" }, "subject": { "type": "string" }, "subjectScheme": { "type": "string" } diff --git a/app/models/schemas/doi/description.json b/app/models/schemas/doi/description.json index 6fe24221c..af13ea0fe 100644 --- a/app/models/schemas/doi/description.json +++ b/app/models/schemas/doi/description.json @@ -13,8 +13,8 @@ "$ref": "language.json" } }, + "additionalProperties": false, "dependentRequired": { "description": ["descriptionType"] - }, - "additionalProperties": false + } } \ No newline at end of file diff --git a/app/models/schemas/doi/identifier.json b/app/models/schemas/doi/identifier.json index 72001439e..675357b2e 100644 --- a/app/models/schemas/doi/identifier.json +++ b/app/models/schemas/doi/identifier.json @@ -1,7 +1,6 @@ { "title": "Doi", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "string", - "pattern": "^.*$", + "type": [ "string", "null" ], "description": "A DOI (Digital Object Identifier) registered by a DataCite Member. The format should be 10.21384/foo." } \ No newline at end of file diff --git a/app/models/schemas/doi/language.json b/app/models/schemas/doi/language.json index 01032b8ab..a7b931fdd 100644 --- a/app/models/schemas/doi/language.json +++ b/app/models/schemas/doi/language.json @@ -1,5 +1,5 @@ { "title": "Language", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": ["string", "null" ] -} + "type": [ "string", "null" ] +} \ No newline at end of file From 681654aeea51e949bad4a498d5e5dddfc03a818f Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 15 Dec 2025 22:29:50 -0500 Subject: [PATCH 71/79] Appease rubocop --- app/models/doi.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/doi.rb b/app/models/doi.rb index 742c03465..a3046ea8b 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -1773,7 +1773,7 @@ def sizes=(value) def dates=(value) write_attribute(:dates, Array.wrap(value)) end - + def language=(value) write_attribute(:language, value) end From 7a1071ebb905faf328995067c68b1585d9b2bce2 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 15 Dec 2025 22:41:29 -0500 Subject: [PATCH 72/79] Fixes - language --- app/models/doi.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/models/doi.rb b/app/models/doi.rb index a3046ea8b..107bed947 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -161,7 +161,7 @@ def schema_file_path(schema_name) validates :contributors, if: proc { |doi| doi.validate_json_attribute?(:contributors) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("contributors") } }, unless: :only_validate validates :dates, if: proc { |doi| doi.validate_json_attribute?(:dates) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("dates") } }, unless: :only_validate # temporarily commenting out because of problems with language validation. - validates :language, if: proc { |doi| doi.validate_json_attribute?(:language) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("language") } }, unless: :only_validate + # validates :language, if: proc { |doi| doi.validate_json_attribute?(:language) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("language") } }, unless: :only_validate validates :resource_type, if: proc { |doi| doi.validate_json_attribute?(:resource_type) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("resource_type") } }, unless: :only_validate validates :alternate_identifiers, if: proc { |doi| doi.validate_json_attribute?(:alternate_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("alternate_identifiers") } }, unless: :only_validate validates :related_identifiers, if: proc { |doi| doi.validate_json_attribute?(:related_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_identifiers") } }, unless: :only_validate @@ -1774,10 +1774,6 @@ def dates=(value) write_attribute(:dates, Array.wrap(value)) end - def language=(value) - write_attribute(:language, value) - end - def subjects=(value) write_attribute(:subjects, Array.wrap(value)) end From bb739f4871a9b079c7771cbded233bdab3fb89d3 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 15 Dec 2025 22:50:45 -0500 Subject: [PATCH 73/79] Fix --- app/models/schemas/client/subjects.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/schemas/client/subjects.json b/app/models/schemas/client/subjects.json index 69f9da678..bf4302384 100644 --- a/app/models/schemas/client/subjects.json +++ b/app/models/schemas/client/subjects.json @@ -14,7 +14,7 @@ ] }, "lang": { - "$ref": "language.json" + "$ref": "../doi/language.json" }, "subject": { "type": "string" }, "subjectScheme": { "type": "string" } From 74d03754a3dc7af62cbf493d680ede72ebc7b642 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 22 Dec 2025 10:58:09 -0500 Subject: [PATCH 74/79] Fix language validation - add raw_language. --- app/models/doi.rb | 8 ++++++-- app/models/schemas/doi/language.json | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/models/doi.rb b/app/models/doi.rb index 8361a17f1..b0382b5f0 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -161,8 +161,7 @@ def schema_file_path(schema_name) validates :subjects, if: proc { |doi| doi.validate_json_attribute?(:subjects) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("subjects") } }, unless: :only_validate validates :contributors, if: proc { |doi| doi.validate_json_attribute?(:contributors) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("contributors") } }, unless: :only_validate validates :dates, if: proc { |doi| doi.validate_json_attribute?(:dates) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("dates") } }, unless: :only_validate - # temporarily commenting out because of problems with language validation. - # validates :language, if: proc { |doi| doi.validate_json_attribute?(:language) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("language") } }, unless: :only_validate + validates :raw_language, if: proc { |doi| doi.validate_json_attribute?(:raw_language) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("language") }}, unless: :only_validate validates :resource_type, if: proc { |doi| doi.validate_json_attribute?(:resource_type) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("resource_type") } }, unless: :only_validate validates :alternate_identifiers, if: proc { |doi| doi.validate_json_attribute?(:alternate_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("alternate_identifiers") } }, unless: :only_validate validates :related_identifiers, if: proc { |doi| doi.validate_json_attribute?(:related_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_identifiers") } }, unless: :only_validate @@ -175,6 +174,11 @@ def schema_file_path(schema_name) validates :funding_references, if: proc { |doi| doi.validate_json_attribute?(:funding_references) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("funding_references") } }, unless: :only_validate validates :related_items, if: proc { |doi| doi.validate_json_attribute?(:related_items) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_items") } }, unless: :only_validate + # See https://github.com/mirego/activerecord_json_validator for an explanation of why this must be done. + def raw_language + self[:language] + end + after_commit :update_url, on: %i[create update] after_commit :update_media, on: %i[create update] diff --git a/app/models/schemas/doi/language.json b/app/models/schemas/doi/language.json index a7b931fdd..b29cf1a77 100644 --- a/app/models/schemas/doi/language.json +++ b/app/models/schemas/doi/language.json @@ -1,5 +1,6 @@ { "title": "Language", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": [ "string", "null" ] + "type": [ "string", "null" ], + "pattern": "^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$" } \ No newline at end of file From 86c22a7e181d77685ec7225461003408f28aefb2 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Mon, 22 Dec 2025 11:03:57 -0500 Subject: [PATCH 75/79] Appease rubocop --- app/models/doi.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/doi.rb b/app/models/doi.rb index b0382b5f0..db369ddf4 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -161,7 +161,7 @@ def schema_file_path(schema_name) validates :subjects, if: proc { |doi| doi.validate_json_attribute?(:subjects) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("subjects") } }, unless: :only_validate validates :contributors, if: proc { |doi| doi.validate_json_attribute?(:contributors) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("contributors") } }, unless: :only_validate validates :dates, if: proc { |doi| doi.validate_json_attribute?(:dates) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("dates") } }, unless: :only_validate - validates :raw_language, if: proc { |doi| doi.validate_json_attribute?(:raw_language) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("language") }}, unless: :only_validate + validates :raw_language, if: proc { |doi| doi.validate_json_attribute?(:raw_language) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("language") } }, unless: :only_validate validates :resource_type, if: proc { |doi| doi.validate_json_attribute?(:resource_type) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("resource_type") } }, unless: :only_validate validates :alternate_identifiers, if: proc { |doi| doi.validate_json_attribute?(:alternate_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("alternate_identifiers") } }, unless: :only_validate validates :related_identifiers, if: proc { |doi| doi.validate_json_attribute?(:related_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_identifiers") } }, unless: :only_validate From d3f20564c27cf906944074a5faffdd7294ef6713 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Tue, 6 Jan 2026 08:12:16 -0500 Subject: [PATCH 76/79] Add test. --- spec/requests/datacite_dois/post_spec.rb | 109 +++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/spec/requests/datacite_dois/post_spec.rb b/spec/requests/datacite_dois/post_spec.rb index 048f5d1e5..90a3de37a 100644 --- a/spec/requests/datacite_dois/post_spec.rb +++ b/spec/requests/datacite_dois/post_spec.rb @@ -2,6 +2,7 @@ require "rails_helper" include Passwordable +require "pp" describe DataciteDoisController, type: :request, vcr: true do let(:admin) { create(:provider, symbol: "ADMIN") } @@ -2427,4 +2428,112 @@ end end end + +# json-schema testing + + describe "POST /dois - json-schema" do + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "types" => { "bibtex" => "article", "citeproc" => "article-journal", "resourceType" => "BlogPosting", "resourceTypeGeneral" => "Text", "ris" => "RPRT", "schemaOrg" => "ScholarlyArticle" }, + "titles" => [{ "title" => "Eating your own Dog Food" }], + "publisher" => "DataCite", + "publicationYear" => 2016, + "creators" => [{ "familyName" => "Fenner", "givenName" => "Martin", "nameIdentifiers" => [{ "nameIdentifier" => "https://orcid.org/0000-0003-1419-2405", "nameIdentifierScheme" => "ORCID", "schemeUri" => "https://orcid.org" }], "name" => "Fenner, Martin", "nameType" => "Personal" }], + "language" => "en", + "alternateIdentifiers" => [{ "alternateIdentifier" => "123", "alternateIdentifierType" => "Repository ID" }], + "rightsList" => [{ "rights" => "Creative Commons Attribution 3.0", "rightsUri" => "http://creativecommons.org/licenses/by/3.0/", "lang" => "en" }], + "sizes" => ["4 kB", "12.6 MB"], + "formats" => ["application/pdf", "text/csv"], + "version" => "1.1", + "fundingReferences" => [{ "funderIdentifier" => "https://doi.org/10.13039/501100009053", "funderIdentifierType" => "Crossref Funder ID", "funderName" => "The Wellcome Trust DBT India Alliance" }], + "source" => "test", + "event" => "publish", + "relatedItems" => [{ + "contributors" => [{ "name" => "Smithson, James", + "contributorType" => "ProjectLeader", + "givenName" => "James", + "familyName" => "Smithson", + "nameType" => "Personal" + }], + "creators" => [{ "name" => "Smith, John", + "nameType" => "Personal", + "givenName" => "John", + "familyName" => "Smith", + }], + "firstPage" => "249", + "lastPage" => "264", + "publicationYear" => "2018", + "relatedItemIdentifier" => { "relatedItemIdentifier" => "10.1016/j.physletb.2017.11.044", + "relatedItemIdentifierType" => "DOI", + "relatedMetadataScheme" => "citeproc+json", + "schemeURI" => "https://github.com/citation-style-language/schema/raw/master/csl-data.json", + "schemeType" => "URL" + }, + "relatedItemType" => "Journal", + "relationType" => "HasMetadata", + "titles" => [{ "title" => "Physics letters / B" }], + "volume" => "776" + }], + }, + }, + } + end + + before do + VCR.eject_cassette + VCR.turn_off! + WebMock.allow_net_connect! + end + + context "json-schema - validate language field - VALID" do + before do + valid_attributes["data"]["attributes"]["language"] = "fr" + end + + it "creates a Doi" do + puts "--------------------------" + puts "LANGUAGE IS:" + puts valid_attributes["data"]["attributes"]["language"] + puts "--------------------------" + + VCR.turned_off do + post "/dois", valid_attributes, headers + end + + expect(last_response.status).to eq(201) + end + end + + context "json-schema - validate language field - INVALID" do + before do + valid_attributes["data"]["attributes"]["language"] = "fr!800-afs" + end + + it "creates a Doi" do + puts "--------------------------" + puts "LANGUAGE IS:" + puts valid_attributes["data"]["attributes"]["language"] + puts "--------------------------" + + VCR.turned_off do + post "/dois", valid_attributes, headers + end + + puts "--------------------------" + puts "ERRORS ARE:" + puts json.dig("errors") + puts "--------------------------" + + expect(last_response.status).to eq(422) + expect(json.dig("errors")).to eq([ + {"source"=>"metadata", "title"=>"Is invalid", "uid"=>"10.14454/10703"} + ]) + end + end + end end From 64206c301843d64255e00f9b145b3c8ca9e0991e Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Tue, 6 Jan 2026 08:53:49 -0500 Subject: [PATCH 77/79] Review comment - fix --- app/models/doi.rb | 2 +- app/models/schemas/doi/resource_type.json | 4 +--- spec/requests/datacite_dois/post_spec.rb | 21 +++------------------ 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/app/models/doi.rb b/app/models/doi.rb index db369ddf4..a7e87ab8d 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -162,7 +162,7 @@ def schema_file_path(schema_name) validates :contributors, if: proc { |doi| doi.validate_json_attribute?(:contributors) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("contributors") } }, unless: :only_validate validates :dates, if: proc { |doi| doi.validate_json_attribute?(:dates) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("dates") } }, unless: :only_validate validates :raw_language, if: proc { |doi| doi.validate_json_attribute?(:raw_language) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("language") } }, unless: :only_validate - validates :resource_type, if: proc { |doi| doi.validate_json_attribute?(:resource_type) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("resource_type") } }, unless: :only_validate + validates :types, if: proc { |doi| doi.validate_json_attribute?(:resource_type) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("resource_type") } }, unless: :only_validate validates :alternate_identifiers, if: proc { |doi| doi.validate_json_attribute?(:alternate_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("alternate_identifiers") } }, unless: :only_validate validates :related_identifiers, if: proc { |doi| doi.validate_json_attribute?(:related_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_identifiers") } }, unless: :only_validate validates :sizes, if: proc { |doi| doi.validate_json_attribute?(:sizes) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("sizes") } }, unless: :only_validate diff --git a/app/models/schemas/doi/resource_type.json b/app/models/schemas/doi/resource_type.json index d1b7550dd..a276587b4 100644 --- a/app/models/schemas/doi/resource_type.json +++ b/app/models/schemas/doi/resource_type.json @@ -9,7 +9,5 @@ "required": [ "resourceType", "resourceTypeGeneral" - ], - "additionalProperties": false, - "minitems": 1 + ] } diff --git a/spec/requests/datacite_dois/post_spec.rb b/spec/requests/datacite_dois/post_spec.rb index 90a3de37a..7d2cb1a13 100644 --- a/spec/requests/datacite_dois/post_spec.rb +++ b/spec/requests/datacite_dois/post_spec.rb @@ -2429,7 +2429,7 @@ end end -# json-schema testing + # json-schema testing describe "POST /dois - json-schema" do let(:valid_attributes) do @@ -2490,17 +2490,12 @@ WebMock.allow_net_connect! end - context "json-schema - validate language field - VALID" do + context "json-schema - VALID doi" do before do valid_attributes["data"]["attributes"]["language"] = "fr" end it "creates a Doi" do - puts "--------------------------" - puts "LANGUAGE IS:" - puts valid_attributes["data"]["attributes"]["language"] - puts "--------------------------" - VCR.turned_off do post "/dois", valid_attributes, headers end @@ -2515,23 +2510,13 @@ end it "creates a Doi" do - puts "--------------------------" - puts "LANGUAGE IS:" - puts valid_attributes["data"]["attributes"]["language"] - puts "--------------------------" - VCR.turned_off do post "/dois", valid_attributes, headers end - puts "--------------------------" - puts "ERRORS ARE:" - puts json.dig("errors") - puts "--------------------------" - expect(last_response.status).to eq(422) expect(json.dig("errors")).to eq([ - {"source"=>"metadata", "title"=>"Is invalid", "uid"=>"10.14454/10703"} + { "source" => "metadata", "title" => "Is invalid", "uid" => "10.14454/10703" } ]) end end From 1162d939c6eaa491954180883a5552814b0445cb Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Tue, 6 Jan 2026 15:54:42 -0500 Subject: [PATCH 78/79] Types/resourceType validation --- app/models/doi.rb | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/app/models/doi.rb b/app/models/doi.rb index a7e87ab8d..94aea802c 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -2,6 +2,8 @@ require "maremma" require "benchmark" +require "byebug" +require "pp" class Doi < ApplicationRecord INVALID_SCHEMAS = %w[ @@ -161,8 +163,6 @@ def schema_file_path(schema_name) validates :subjects, if: proc { |doi| doi.validate_json_attribute?(:subjects) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("subjects") } }, unless: :only_validate validates :contributors, if: proc { |doi| doi.validate_json_attribute?(:contributors) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("contributors") } }, unless: :only_validate validates :dates, if: proc { |doi| doi.validate_json_attribute?(:dates) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("dates") } }, unless: :only_validate - validates :raw_language, if: proc { |doi| doi.validate_json_attribute?(:raw_language) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("language") } }, unless: :only_validate - validates :types, if: proc { |doi| doi.validate_json_attribute?(:resource_type) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("resource_type") } }, unless: :only_validate validates :alternate_identifiers, if: proc { |doi| doi.validate_json_attribute?(:alternate_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("alternate_identifiers") } }, unless: :only_validate validates :related_identifiers, if: proc { |doi| doi.validate_json_attribute?(:related_identifiers) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_identifiers") } }, unless: :only_validate validates :sizes, if: proc { |doi| doi.validate_json_attribute?(:sizes) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("sizes") } }, unless: :only_validate @@ -173,12 +173,26 @@ def schema_file_path(schema_name) validates :geolocations, if: proc { |doi| doi.validate_json_attribute?(:geolocations) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("geolocations") } }, unless: :only_validate validates :funding_references, if: proc { |doi| doi.validate_json_attribute?(:funding_references) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("funding_references") } }, unless: :only_validate validates :related_items, if: proc { |doi| doi.validate_json_attribute?(:related_items) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_items") } }, unless: :only_validate + + validates :raw_language, presence: true, if: proc { |doi| doi.validate_json_attribute?(:raw_language) }, json: { + message: ->(errors) { errors }, + schema: lambda { schema_file_path("language") } + }, unless: :only_validate + + validates :raw_types, if: proc { |doi| doi.validate_json_attribute?(:raw_types) }, json: { + message: ->(errors) { errors }, + schema: lambda { schema_file_path("resource_type") }, + }, unless: :only_validate # See https://github.com/mirego/activerecord_json_validator for an explanation of why this must be done. def raw_language self[:language] end + def raw_types + self[:types] + end + after_commit :update_url, on: %i[create update] after_commit :update_media, on: %i[create update] From 3232373f2a4497264b7775076e2f0373481e71c4 Mon Sep 17 00:00:00 2001 From: Suzanne Vogt Date: Tue, 6 Jan 2026 15:55:49 -0500 Subject: [PATCH 79/79] Appease rubocop. --- app/models/doi.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/doi.rb b/app/models/doi.rb index 94aea802c..4826b0cd5 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -173,13 +173,13 @@ def schema_file_path(schema_name) validates :geolocations, if: proc { |doi| doi.validate_json_attribute?(:geolocations) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("geolocations") } }, unless: :only_validate validates :funding_references, if: proc { |doi| doi.validate_json_attribute?(:funding_references) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("funding_references") } }, unless: :only_validate validates :related_items, if: proc { |doi| doi.validate_json_attribute?(:related_items) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("related_items") } }, unless: :only_validate - + validates :raw_language, presence: true, if: proc { |doi| doi.validate_json_attribute?(:raw_language) }, json: { - message: ->(errors) { errors }, + message: ->(errors) { errors }, schema: lambda { schema_file_path("language") } }, unless: :only_validate - validates :raw_types, if: proc { |doi| doi.validate_json_attribute?(:raw_types) }, json: { + validates :raw_types, if: proc { |doi| doi.validate_json_attribute?(:raw_types) }, json: { message: ->(errors) { errors }, schema: lambda { schema_file_path("resource_type") }, }, unless: :only_validate