diff --git a/Gemfile b/Gemfile index 2dc8b41fe..a5efa64fe 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", "~> 3.8" gem "audited", "~> 5.8" gem "aws-sdk-core", "~> 3.226" diff --git a/Gemfile.lock b/Gemfile.lock index 4b9fa5d35..e97bbe9e0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -63,9 +63,9 @@ GEM activemodel (= 7.2.3) activesupport (= 7.2.3) 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.2.3) actionpack (= 7.2.3) activejob (= 7.2.3) @@ -276,8 +276,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) @@ -389,12 +387,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.5.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) @@ -775,7 +772,6 @@ GEM unicode_utils (1.4.0) uniform_notifier (1.16.0) uri (1.1.1) - uri_template (0.7.0) useragent (0.16.11) uuid (2.3.9) macaddr (~> 1.0) @@ -805,7 +801,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 (~> 3.8) audited (~> 5.8) aws-sdk-core (~> 3.226) diff --git a/app/models/doi.rb b/app/models/doi.rb index 3b87366f0..962a4a7a7 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] @@ -113,16 +121,13 @@ 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)) + def validate_json_attribute?(attribute) + validatable? && !self[attribute].nil? && !INVALID_SCHEMAS.include?(self.schema_version) end - validates :publisher_obj, if: ->(doi) { validate_publisher_obj?(doi) }, json: json_schema_validation + def schema_file_path(schema_name) + Rails.root.join("app", "models", "schemas", "doi", "#{schema_name}.json") + end # 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 @@ -149,6 +154,46 @@ def validate_publisher_obj?(doi) validate :check_geo_locations, if: :geo_locations? validate :check_language, if: :language? + # JSON-SCHEMA VALIDATION + # temporarily commenting out this 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 :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 :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 :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 + + 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] diff --git a/app/models/schemas/client/subjects.json b/app/models/schemas/client/subjects.json index 3e204ecd6..7937e7f2d 100644 --- a/app/models/schemas/client/subjects.json +++ b/app/models/schemas/client/subjects.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "array", "minItems": 0, "items": { @@ -14,10 +14,7 @@ ] }, "lang": { - "type": [ - "string", - "null" - ] + "$ref": "../doi/language.json" }, "subject": { "type": "string" }, "subjectScheme": { "type": "string" } diff --git a/app/models/schemas/doi/affiliation.json b/app/models/schemas/doi/affiliation.json new file mode 100644 index 000000000..d264ecd97 --- /dev/null +++ b/app/models/schemas/doi/affiliation.json @@ -0,0 +1,21 @@ +{ + "title": "Affiliation", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Uniquely identifies an affiliation, according to various identifier schemes.", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "affiliationIdentifier": { + "type": "string" + }, + "affiliationIdentifierScheme": { + "type": "string" + }, + "schemeUri": { + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/app/models/schemas/doi/affiliations.json b/app/models/schemas/doi/affiliations.json new file mode 100644 index 000000000..5becde88f --- /dev/null +++ b/app/models/schemas/doi/affiliations.json @@ -0,0 +1,8 @@ +{ + "title": "Affiliations", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "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..8a5b51bd1 --- /dev/null +++ b/app/models/schemas/doi/alternate_identifier.json @@ -0,0 +1,16 @@ +{ + "title": "AlternateIdentifier", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "An identifier or identifiers other than the primary Identifier applied to the resource being registered. This may be any alphanumeric string which is unique within its domain of issue. May be used for local identifiers. AlternateIdentifier should be used for another identifier of the same instance (same location, same file).", + "type": "object", + "properties": { + "alternateIdentifier": { + "type": "string" + }, + "alternateIdentifierType": { + "type": "string" + } + }, + "required": ["alternateIdentifierType"], + "additionalProperties": false +} diff --git a/app/models/schemas/doi/alternate_identifiers.json b/app/models/schemas/doi/alternate_identifiers.json new file mode 100644 index 000000000..7bbe55300 --- /dev/null +++ b/app/models/schemas/doi/alternate_identifiers.json @@ -0,0 +1,8 @@ +{ + "title": "AlternateIdentifiers", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "items": { + "$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..d6595447c --- /dev/null +++ b/app/models/schemas/doi/contributor.json @@ -0,0 +1,16 @@ +{ + "title": "Contributor", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The institution or person responsible for collecting, creating, or otherwise contributing to the development of the dataset.", + "type": "object", + "$ref": "contributor_base.json", + "properties": { + "nameIdentifiers": { + "$ref": "name_identifiers.json" + }, + "affiliation": { + "$ref": "affiliations.json" + } + }, + "unevaluatedProperties": false +} diff --git a/app/models/schemas/doi/contributor_base.json b/app/models/schemas/doi/contributor_base.json new file mode 100644 index 000000000..ddcdc2e32 --- /dev/null +++ b/app/models/schemas/doi/contributor_base.json @@ -0,0 +1,30 @@ +{ + "title": "ContributorBase", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The institution or person responsible for collecting, creating, or otherwise contributing to the development of the dataset.", + "type": "object", + "properties": { + "name": { + "$ref": "definitions.json#/$defs/nonemptycontentStringType" + }, + "nameType": { + "$ref": "controlled_vocabularies/name_type.json" + }, + "lang": { + "$ref": "definitions.json#/$defs/language" + }, + "givenName": { + "type": "string" + }, + "familyName": { + "type": "string" + }, + "contributorType": { + "$ref": "controlled_vocabularies/contributor_type.json" + } + }, + "required": [ + "name", + "contributorType" + ] +} diff --git a/app/models/schemas/doi/contributors.json b/app/models/schemas/doi/contributors.json new file mode 100644 index 000000000..314dfbf6e --- /dev/null +++ b/app/models/schemas/doi/contributors.json @@ -0,0 +1,8 @@ +{ + "title": "Contributors", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "items": { + "$ref": "contributor.json" + } +} \ 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..5570d4da5 --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/contributor_type.json @@ -0,0 +1,30 @@ +{ + "title": "ContributorType", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The type of contributor of the resource.", + "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" + ] +} 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..6008e465d --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/date_type.json @@ -0,0 +1,20 @@ +{ + "title": "DateType", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The type of date. Use RKMS‐ISO8601 standard for depicting date ranges.To indicate the end of an embargo period, use Available. To indicate the start of an embargo period, use Submitted or Accepted, as appropriate.", + "type": "string", + "enum": [ + "Accepted", + "Available", + "Copyrighted", + "Collected", + "Coverage", + "Created", + "Issued", + "Submitted", + "Updated", + "Valid", + "Withdrawn", + "Other" + ] +} 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..8322cac42 --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/description_type.json @@ -0,0 +1,14 @@ +{ + "title": "DescriptionType", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The type of description of the resource.", + "type": "string", + "enum": [ + "Abstract", + "Methods", + "SeriesInformation", + "TableOfContents", + "TechnicalInfo", + "Other" + ] +} 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..3dac3b449 --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/funder_identifier_type.json @@ -0,0 +1,14 @@ +{ + "title": "FunderIdentifierType", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The type of the funderIdentifier.", + "type": "string", + "enum": [ + "ISNI", + "GRID", + "ROR", + "Crossref Funder ID", + "Other" + ] +} + 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..071803d03 --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/name_type.json @@ -0,0 +1,9 @@ +{ + "title": "NameType", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string", + "enum": [ + "Organizational", + "Personal" + ] +} 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..8b8a90484 --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/number_type.json @@ -0,0 +1,11 @@ +{ + "title": "NumberType", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string", + "enum": [ + "Article", + "Chapter", + "Report", + "Other" + ] +} 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..a822f8741 --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/related_identifier_type.json @@ -0,0 +1,31 @@ +{ + "title": "RelatedIdentifierType", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The type of RelatedIdentifier.", + "type": "string", + "enum": [ + "ARK", + "arXiv", + "bibcode", + "CSTR", + "DOI", + "EAN13", + "EISSN", + "Handle", + "IGSN", + "ISBN", + "ISSN", + "ISTC", + "LISSN", + "LSID", + "PMID", + "PURL", + "RAiD", + "RRID", + "SWHID", + "UPC", + "URL", + "URN", + "w3id" + ] +} 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..abe7b98fd --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/related_item_type.json @@ -0,0 +1,42 @@ +{ + "title": "RelatedItemType", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The general type of a related item.", + "type": "string", + "enum": [ + "Audiovisual", + "Award", + "Book", + "BookChapter", + "Collection", + "ComputationalNotebook", + "ConferencePaper", + "ConferenceProceeding", + "DataPaper", + "Dataset", + "Dissertation", + "Event", + "Image", + "InteractiveResource", + "Instrument", + "Journal", + "JournalArticle", + "Model", + "OutputManagementPlan", + "PeerReview", + "PhysicalObject", + "Poster", + "Preprint", + "Presentation", + "Project", + "Report", + "Service", + "Software", + "Sound", + "Standard", + "StudyRegistration", + "Text", + "Workflow", + "Other" + ] +} 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..c817db366 --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/relation_type.json @@ -0,0 +1,47 @@ +{ + "title": "RelationType", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Description of the relationship of the resource being registered (A) and the related resource (B).", + "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", + "Other" + ] +} 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..c01d742c3 --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/resource_type_general.json @@ -0,0 +1,42 @@ +{ + "title": "ResourceTypeGeneral", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The general type of a resource.", + "type": "string", + "enum": [ + "Audiovisual", + "Award", + "Book", + "BookChapter", + "Collection", + "ComputationalNotebook", + "ConferencePaper", + "ConferenceProceeding", + "DataPaper", + "Dataset", + "Dissertation", + "Event", + "Image", + "InteractiveResource", + "Instrument", + "Journal", + "JournalArticle", + "Model", + "OutputManagementPlan", + "PeerReview", + "PhysicalObject", + "Poster", + "Preprint", + "Presentation", + "Project", + "Report", + "Service", + "Software", + "Sound", + "Standard", + "StudyRegistration", + "Text", + "Workflow", + "Other" + ] +} diff --git a/app/models/schemas/doi/controlled_vocabularies/standard_values.json b/app/models/schemas/doi/controlled_vocabularies/standard_values.json new file mode 100644 index 000000000..3c6cf47b3 --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/standard_values.json @@ -0,0 +1,18 @@ +{ + "title": "StandardValues", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Standard values for unknown information.", + "type": "string", + "enum": [ + ":unac", + ":unal", + ":unap", + ":unas", + ":unav", + ":unkn", + ":none", + ":null", + ":tba", + ":etal" + ] +} 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..ac074ebb3 --- /dev/null +++ b/app/models/schemas/doi/controlled_vocabularies/title_type.json @@ -0,0 +1,12 @@ +{ + "title": "TitleType", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The type of title of the resource.", + "type": "string", + "enum": [ + "AlternativeTitle", + "Subtitle", + "TranslatedTitle", + "Other" + ] +} diff --git a/app/models/schemas/doi/creator.json b/app/models/schemas/doi/creator.json new file mode 100644 index 000000000..3b719cb6a --- /dev/null +++ b/app/models/schemas/doi/creator.json @@ -0,0 +1,16 @@ +{ + "title": "Creator", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The main researchers involved working on the data, or the authors of the publication in priority order. May be a corporate/institutional or personal name.", + "type": "object", + "$ref": "creator_base.json", + "properties": { + "nameIdentifiers": { + "$ref": "name_identifiers.json" + }, + "affiliation": { + "$ref": "affiliations.json" + } + }, + "unevaluatedProperties": false +} diff --git a/app/models/schemas/doi/creator_base.json b/app/models/schemas/doi/creator_base.json new file mode 100644 index 000000000..eea8365f0 --- /dev/null +++ b/app/models/schemas/doi/creator_base.json @@ -0,0 +1,23 @@ +{ + "title": "CreatorBase", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The main researchers involved working on the data, or the authors of the publication in priority order. May be a corporate/institutional or personal name.", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "nameType": { + "$ref": "controlled_vocabularies/name_type.json" + }, + "lang": { + "$ref": "definitions.json#/$defs/language" + }, + "givenName": { + "type": "string" + }, + "familyName": { + "type": "string" + } + } +} diff --git a/app/models/schemas/doi/creators.json b/app/models/schemas/doi/creators.json new file mode 100644 index 000000000..3115c6a9a --- /dev/null +++ b/app/models/schemas/doi/creators.json @@ -0,0 +1,9 @@ +{ + "title": "Creators", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "minItems": 1, + "items": { + "$ref": "creator.json" + } +} diff --git a/app/models/schemas/doi/date.json b/app/models/schemas/doi/date.json new file mode 100644 index 000000000..d84da775f --- /dev/null +++ b/app/models/schemas/doi/date.json @@ -0,0 +1,21 @@ +{ + "title": "Date", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Different dates relevant to the work.", + "type": "object", + "properties": { + "date": { + "type": "string" + }, + "dateType": { + "$ref": "controlled_vocabularies/date_type.json" + }, + "dateInformation": { + "type": "string" + } + }, + "required": [ + "dateType" + ], + "additionalProperties": false +} diff --git a/app/models/schemas/doi/dates.json b/app/models/schemas/doi/dates.json new file mode 100644 index 000000000..4907c0b07 --- /dev/null +++ b/app/models/schemas/doi/dates.json @@ -0,0 +1,8 @@ +{ + "title": "Dates", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "items": { + "$ref": "date.json" + } +} diff --git a/app/models/schemas/doi/definitions.json b/app/models/schemas/doi/definitions.json new file mode 100644 index 000000000..1c9f122f7 --- /dev/null +++ b/app/models/schemas/doi/definitions.json @@ -0,0 +1,109 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "definitions.json", + "Description": "Definitions for re-use in the DOI metadata schema.", + "$defs": { + "nonemptycontentStringType": { + "minLength": 1, + "type": "string" + }, + "edtf": { + "type": "string", + "description": "A date matching W3CDTF / EDTF format, RKMS-ISO8601 date range formats, or a standard value indicating unknown or unavailable information.", + "anyOf": [ + { + "description": "Pattern for iso8601 dateTime ", + "pattern": "(-)?[0-9]{4}(-[0-9]{2})?(-[0-9]{2})?(T([0-9]{2}:){2}[0-9]{2}Z)?" + }, + { + "description": "The following pattern is for year (yyyy) or year-month (yyyy-mm)", + "pattern": "\\d{2}(\\d{2}|\\?\\?|\\d(\\d|\\?))(-(\\d{2}|\\?\\?))?~?\\??" + }, + { + "description": "The following pattern is for yearMonthDay - yyyymmdd, where 'dd' may be '??' so '200412??' means 'some day during the month of 12/2004'", + "pattern": "\\d{6}(\\d{2}|\\?\\?)?~?\\??" + }, + { + "description": "The following pattern is for date and time with T separator:'yyyymmddThhmmss'.", + "pattern": "\\d{8}T\\d{6}" + }, + { + "description": "The following pattern is for a date range. in years: 'yyyy/yyyy'; or year/month: yyyy-mm/yyyy-mm, or year/month/day: yyyy-mm-dd/yyyy-mm-dd.", + "pattern": "((-)?(\\d{4}(-\\d{2})?(-\\d{2})?)|unknown)/((-)?(\\d{4}(-\\d{2})?(-\\d{2})?)|unknown|open)" + }, + { + "description": "A reference to the controlled vocabulary of standard values indicating unknown or unavailable information for dates.", + "$ref": "controlled_vocabularies/standard_values.json" + } + ] + }, + "yearType": { + "type": [ "integer", "string" ], + "oneOf": [ + { + "pattern": "[\\d]{4}", + "type": "string" + }, + { + "type": "integer", + "minimum": 1000, + "maximum": 9999 + } + ] + }, + "language": { + "type": [ "string", "null"], + "pattern": "^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$" + }, + "longitudeType": { + "maximum": 180, + "minimum": -180, + "type": "number" + }, + "latitudeType": { + "maximum": 90, + "minimum": -90, + "type": "number" + }, + "box": { + "type": "object", + "properties": { + "westBoundLongitude": { + "$ref": "#/$defs/longitudeType" + }, + "eastBoundLongitude": { + "$ref": "#/$defs/longitudeType" + }, + "southBoundLatitude": { + "$ref": "#/$defs/latitudeType" + }, + "northBoundLatitude": { + "$ref": "#/$defs/latitudeType" + } + }, + "required": [ + "westBoundLongitude", + "eastBoundLongitude", + "southBoundLatitude", + "northBoundLatitude" + ], + "additionalProperties": false + }, + "point": { + "type": "object", + "properties": { + "pointLongitude": { + "$ref": "#/$defs/longitudeType" + }, + "pointLatitude": { + "$ref": "#/$defs/latitudeType" + } + }, + "required": [ + "pointLongitude", + "pointLatitude" + ], + "additionalProperties": false + } + } +} diff --git a/app/models/schemas/doi/description.json b/app/models/schemas/doi/description.json new file mode 100644 index 000000000..31bae9f6e --- /dev/null +++ b/app/models/schemas/doi/description.json @@ -0,0 +1,21 @@ +{ + "title": "Description", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "All additional information that does not fit in any of the other categories. May be used for technical information. It is a best practice to supply a description.", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "descriptionType": { + "$ref": "controlled_vocabularies/description_type.json" + }, + "lang": { + "$ref": "definitions.json#/$defs/language" + } + }, + "additionalProperties": false, + "required": [ + "descriptionType" + ] +} \ 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..1158c6555 --- /dev/null +++ b/app/models/schemas/doi/descriptions.json @@ -0,0 +1,8 @@ +{ + "title": "Descriptions", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "items": { + "$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..6179ed4e7 --- /dev/null +++ b/app/models/schemas/doi/format.json @@ -0,0 +1,6 @@ +{ + "title": "Format", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Technical format of the resource.", + "type": "string" +} diff --git a/app/models/schemas/doi/formats.json b/app/models/schemas/doi/formats.json new file mode 100644 index 000000000..4fe8c06e8 --- /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": { + "$ref": "format.json" + } +} diff --git a/app/models/schemas/doi/funding_reference.json b/app/models/schemas/doi/funding_reference.json new file mode 100644 index 000000000..44ef89834 --- /dev/null +++ b/app/models/schemas/doi/funding_reference.json @@ -0,0 +1,32 @@ +{ + "title": "FundingReference", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Information about financial support (funding) for the resource being registered.", + "type": "object", + "properties": { + "funderName": { + "$ref": "definitions.json#/$defs/nonemptycontentStringType" + }, + "funderIdentifier": { + "type": "string" + }, + "funderIdentifierType": { + "$ref": "controlled_vocabularies/funder_identifier_type.json" + }, + "schemeUri": { + "type": "string" + }, + "awardNumber": { + "type": "string" + }, + "awardUri": { + "type": "string" + }, + "awardTitle": { + "type": "string" + } + }, + "required": [ + "funderName" ], + "additionalProperties": false +} diff --git a/app/models/schemas/doi/funding_references.json b/app/models/schemas/doi/funding_references.json new file mode 100644 index 000000000..d8b203f87 --- /dev/null +++ b/app/models/schemas/doi/funding_references.json @@ -0,0 +1,8 @@ +{ + "title": "FundingReferences", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "items": { + "$ref": "funding_reference.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..58d2c2f11 --- /dev/null +++ b/app/models/schemas/doi/geo_location.json @@ -0,0 +1,24 @@ +{ + "title": "GeoLocation", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "geoLocationPoint": { + "$ref": "geo_location_point.json" + }, + "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..f5b6e5050 --- /dev/null +++ b/app/models/schemas/doi/geo_location_place.json @@ -0,0 +1,5 @@ +{ + "title": "GeoLocationPlace", + "$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/identifier.json b/app/models/schemas/doi/identifier.json new file mode 100644 index 000000000..6ded2e197 --- /dev/null +++ b/app/models/schemas/doi/identifier.json @@ -0,0 +1,6 @@ +{ + "title": "Identifier", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "A DOI (Digital Object Identifier) registered by a DataCite Member. The format should be 10.21384/foo.", + "$ref": "definitions.json#/$defs/nonemptycontentStringType" +} 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/language.json b/app/models/schemas/doi/language.json new file mode 100644 index 000000000..211628107 --- /dev/null +++ b/app/models/schemas/doi/language.json @@ -0,0 +1,6 @@ +{ + "title": "Language", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Primary language of the resource. Allowed values are taken from IETF BCP 47, ISO 639-1 language codes.", + "$ref": "definitions.json#/$defs/language" +} diff --git a/app/models/schemas/doi/name_identifier.json b/app/models/schemas/doi/name_identifier.json new file mode 100644 index 000000000..5725b500c --- /dev/null +++ b/app/models/schemas/doi/name_identifier.json @@ -0,0 +1,18 @@ +{ + "title": "NameIdentifier", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Uniquely identifies a creator or contributor, according to various identifier schemes.", + "type": "object", + "properties": { + "nameIdentifier": { + "type": "string" + }, + "nameIdentifierScheme": { + "type": ["string", "null"] + }, + "schemeUri": { + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/app/models/schemas/doi/name_identifiers.json b/app/models/schemas/doi/name_identifiers.json new file mode 100644 index 000000000..19cab0205 --- /dev/null +++ b/app/models/schemas/doi/name_identifiers.json @@ -0,0 +1,8 @@ +{ + "title": "NameIdentifiers", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "items": { + "$ref": "name_identifier.json" + } +} \ 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 diff --git a/app/models/schemas/doi/publication_year.json b/app/models/schemas/doi/publication_year.json new file mode 100644 index 000000000..0285ed9c0 --- /dev/null +++ b/app/models/schemas/doi/publication_year.json @@ -0,0 +1,7 @@ +{ + "title": "PublicationYear", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "publicationYear: A required value representing a year in YYYY format.", + "$ref": "definitions.json#/$defs/yearType", + "minLength": 1 +} \ No newline at end of file diff --git a/app/models/schemas/doi/publisher.json b/app/models/schemas/doi/publisher.json index f8b1c66f4..e0763ebb9 100644 --- a/app/models/schemas/doi/publisher.json +++ b/app/models/schemas/doi/publisher.json @@ -1,12 +1,27 @@ { + "title": "Publisher", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The name of the entity that holds, archives, publishes prints, distributes, releases, issues, or produces the resource. This property will be used to formulate the citation, so consider the prominence of the role.", "type": "object", - "$schema": "http://json-schema.org/draft-04/schema#", "properties": { - "name": { "type": "string" }, - "publisherIdentifier": { "type": "string" }, - "publisherIdentifierScheme": { "type": "string" }, - "schemeUri": { "type": "string" }, - "lang": { "type": "string" } + "name": { + "$ref": "definitions.json#/$defs/nonemptycontentStringType" + }, + "publisherIdentifier": { + "type": "string" + }, + "publisherIdentifierScheme": { + "type": "string" + }, + "schemeUri": { + "type": "string" + }, + "lang": { + "$ref": "definitions.json#/$defs/language" + } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "name" + ] } diff --git a/app/models/schemas/doi/related_identifier.json b/app/models/schemas/doi/related_identifier.json new file mode 100644 index 000000000..bc610852f --- /dev/null +++ b/app/models/schemas/doi/related_identifier.json @@ -0,0 +1,33 @@ +{ + "title": "RelatedIdentifiers", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Identifiers of related resources. Use this property to indicate subsets of properties, as appropriate.", + "type": "object", + "properties": { + "relatedIdentifier": { + "type": "string" + }, + "resourceTypeGeneral": { + "$ref": "controlled_vocabularies/resource_type_general.json" + }, + "relatedIdentifierType": { + "$ref": "controlled_vocabularies/related_identifier_type.json" + }, + "relationType": { + "$ref": "controlled_vocabularies/relation_type.json" + }, + "relatedMetadataScheme": { + "$ref": "controlled_vocabularies/relation_type.json" + }, + "schemeUri": { + "type": "string" + }, + "schemeType": { + "type": "string" + }, + "relationTypeInformation": { + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/app/models/schemas/doi/related_identifiers.json b/app/models/schemas/doi/related_identifiers.json new file mode 100644 index 000000000..d52fc123d --- /dev/null +++ b/app/models/schemas/doi/related_identifiers.json @@ -0,0 +1,8 @@ +{ + "title": "RelatedIdentifiers", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "items": { + "$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..34be570a4 --- /dev/null +++ b/app/models/schemas/doi/related_item.json @@ -0,0 +1,68 @@ +{ + "title": "RelatedItem", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Information about a resource related to the one being registered e.g. a journal or book of which the article or chapter is part.", + "type": "object", + "properties": { + "relatedItemIdentifier": { + "$ref": "related_item_identifier.json" + }, + "creators": { + "type": "array", + "items": { + "$ref": "creator_base.json" + } + }, + "titles": { + "$ref": "titles.json", + "minItems": 0 + }, + "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", + "items": { + "$ref": "contributor_base.json" + } + }, + "relatedItemType": { + "$ref": "controlled_vocabularies/resource_type_general.json" + }, + "relationType": { + "$ref": "controlled_vocabularies/relation_type.json" + }, + "relationTypeInformation": { + "type": "string" + } + }, + "additionalProperties": false, + "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..8e5fe8057 --- /dev/null +++ b/app/models/schemas/doi/related_item_identifier.json @@ -0,0 +1,24 @@ +{ + "title": "RelatedItemIdentifier", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The identifier for the related item.", + "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 +} diff --git a/app/models/schemas/doi/related_items.json b/app/models/schemas/doi/related_items.json new file mode 100644 index 000000000..6f5b20e2a --- /dev/null +++ b/app/models/schemas/doi/related_items.json @@ -0,0 +1,8 @@ +{ + "title": "RelatedItems", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "items": { + "$ref": "related_item.json" + } +} \ 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..8e3cb8415 --- /dev/null +++ b/app/models/schemas/doi/resource_type.json @@ -0,0 +1,18 @@ +{ + "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" + } + }, + "additionalProperties": false, + "required": [ + "resourceType", + "resourceTypeGeneral" + ] +} diff --git a/app/models/schemas/doi/rights.json b/app/models/schemas/doi/rights.json new file mode 100644 index 000000000..b082a1fb4 --- /dev/null +++ b/app/models/schemas/doi/rights.json @@ -0,0 +1,26 @@ +{ + "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": { + "$ref": "definitions.json#/$defs/language" + } + }, + "additionalProperties": false +} diff --git a/app/models/schemas/doi/rights_list.json b/app/models/schemas/doi/rights_list.json new file mode 100644 index 000000000..22528d106 --- /dev/null +++ b/app/models/schemas/doi/rights_list.json @@ -0,0 +1,8 @@ +{ + "title": "RightsList", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "items": { + "$ref": "rights.json" + } +} \ 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 new file mode 100644 index 000000000..8f20fc865 --- /dev/null +++ b/app/models/schemas/doi/sizes.json @@ -0,0 +1,9 @@ +{ + "title": "Sizes", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "minItems": 0, + "items": { + "$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..4766c1f28 --- /dev/null +++ b/app/models/schemas/doi/subject.json @@ -0,0 +1,30 @@ +{ + "title": "Subject", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Subject, keywords, classification codes, or key phrases describing the resource.", + "type": "object", + "properties": { + "subject": { + "type": "string" + }, + "subjectScheme": { + "type": "string" + }, + "schemeUri": { + "type": "string" + }, + "valueUri": { + "type": "string" + }, + "classificationCode": { + "type": "string" + }, + "lang": { + "$ref": "definitions.json#/$defs/language" + } + }, + "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 new file mode 100644 index 000000000..c87f6cd40 --- /dev/null +++ b/app/models/schemas/doi/subjects.json @@ -0,0 +1,8 @@ +{ + "title": "Subjects", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "items": { + "$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..5ac25e985 --- /dev/null +++ b/app/models/schemas/doi/title.json @@ -0,0 +1,21 @@ +{ + "title": "Title", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "A name or title by which a resource is known.", + "type": "object", + "properties": { + "title": { + "type": [ "string", "null" ] + }, + "titleType": { + "$ref": "controlled_vocabularies/title_type.json" + }, + "lang": { + "$ref": "definitions.json#/$defs/language" + } + }, + "additionalProperties": false, + "required": [ + "title" + ] +} \ No newline at end of file diff --git a/app/models/schemas/doi/titles.json b/app/models/schemas/doi/titles.json new file mode 100644 index 000000000..7b1def10c --- /dev/null +++ b/app/models/schemas/doi/titles.json @@ -0,0 +1,9 @@ +{ + "title": "Titles", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "array", + "minItems": 1, + "items": { + "$ref": "title.json" + } +} \ No newline at end of file diff --git a/app/models/schemas/doi/version.json b/app/models/schemas/doi/version.json new file mode 100644 index 000000000..3c4f57db7 --- /dev/null +++ b/app/models/schemas/doi/version.json @@ -0,0 +1,6 @@ +{ + "title": "Version", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Version number of the resource. If the primary resource has changed the version number increases.", + "type": [ "number", "string" ] +} diff --git a/app/validators/xml_schema_validator.rb b/app/validators/xml_schema_validator.rb index 7705ac6c2..8f06dc466 100644 --- a/app/validators/xml_schema_validator.rb +++ b/app/validators/xml_schema_validator.rb @@ -40,13 +40,7 @@ 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") diff --git a/spec/concerns/indexable_spec.rb b/spec/concerns/indexable_spec.rb index 380867b5a..69e77cee2 100644 --- a/spec/concerns/indexable_spec.rb +++ b/spec/concerns/indexable_spec.rb @@ -69,9 +69,9 @@ let!(:doi) do create( :doi, - titles: { title: "Soil investigations" }, + titles: [ { title: "Soil investigations" } ], publisher: "Pangaea", - descriptions: { description: "this is a description" }, + descriptions: [{ description: "this is a description", descriptionType: "SeriesInformation" }], aasm_state: "findable", ) end 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/fixtures/files/datacite-user-example.json b/spec/fixtures/files/datacite-user-example.json index 32bcaf699..5a3c2a6f8 100644 --- a/spec/fixtures/files/datacite-user-example.json +++ b/spec/fixtures/files/datacite-user-example.json @@ -11,19 +11,19 @@ "nameType": "Personal", "givenName": "Julia M.", "familyName": "Rovera", + "name": "Rovera, Julia M.", "affiliation": [{ "name": "Drexel University" }], "nameIdentifiers": [{ "schemeUri": "https://orcid.org", - "nameIdentifier": "", + "nameIdentifier": "https://orcid.org/0000-0001-5727-2427", "nameIdentifierScheme": "ORCID" }] }], "titles": [{ "lang": "en", - "title": "The Relationship Among Sport Type, Micronutrient Intake and Bone Mineral Density in an Athlete Population", - "titleType": null + "title": "The Relationship Among Sport Type, Micronutrient Intake and Bone Mineral Density in an Athlete Population" }, { "lang": "en", "title": "", diff --git a/spec/fixtures/files/nasa_error.json b/spec/fixtures/files/nasa_error.json index 82023f91c..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", @@ -31,20 +38,29 @@ "familyName":"Ramasubramanian" } ], - "titles":{ + "titles": [ + { "title":"Tropical Cyclone Satellite Imagery and Wind Speed Dataset" - }, + } + ], "publisher": "Radiant MLHub", "publicationYear": 2020, "types":{ "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" 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/graphql/types/work_type_spec.rb b/spec/graphql/types/work_type_spec.rb index 042d3cdbe..faf5e7c4a 100644 --- a/spec/graphql/types/work_type_spec.rb +++ b/spec/graphql/types/work_type_spec.rb @@ -935,9 +935,9 @@ { "name" => "Kristian Garza", "nameType" => "Personal", - "affiliation" => { + "affiliation" => [{ "name" => "Ruhr-University Bochum, Germany" - } + }] }, { "familyName" => "Garza", @@ -968,14 +968,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" => [ { @@ -1090,50 +1092,52 @@ aasm_state: "findable", creators: [ { - "name" => "Kristian Garza", + "name" => "Garza, Kristian", "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", }, ], contributors: [ { + "name" => "Ross, Cody", "givenName" => "Cody", "familyName" => "Ross", "contributorType" => "Editor", "nameIdentifiers" => - { + [{ "schemeUri": "https://orcid.org", "nameIdentifier": "https://orcid.org/0000-0002-7105-9881", "nameIdentifierScheme": "ORCID" - } + }] }, { + "name" => "Garza, Kristian", "givenName" => "Kristian", "familyName" => "Garza", "contributorType" => "Editor", "nameIdentifiers" => - { + [{ "nameIdentifier" => "https://orcid.org/0000-0003-3484-6875", "nameIdentifierScheme" => "ORCID", "schemeUri" => "https://orcid.org", - }, + }], }, ], ) @@ -1180,7 +1184,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 @@ -1193,7 +1197,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", @@ -1206,12 +1210,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" } @@ -1542,6 +1546,7 @@ create_list(:doi, 5, aasm_state: "findable", types: { "resourceTypeGeneral" => "Text" }, creators: [{ + name: "test_name-5", affiliation: [{ "name": "5", "affiliationIdentifier": "https://ror.org/5", @@ -1555,6 +1560,7 @@ create_list(:doi, 4, aasm_state: "findable", types: { "resourceTypeGeneral" => "JournalArticle" }, creators: [{ + name: "test_name-4", affiliation: [{ "name": "4", "affiliationIdentifier": "https://ror.org/4", @@ -1568,6 +1574,7 @@ create_list(:doi, 3, aasm_state: "findable", types: { "resourceTypeGeneral" => "Image" }, creators: [{ + name: "test_name-3", affiliation: [{ "name": "3", "affiliationIdentifier": "https://ror.org/3", @@ -1581,6 +1588,7 @@ create_list(:doi, 2, aasm_state: "findable", types: { "resourceTypeGeneral" => "PhysicalObject" }, creators: [{ + name: "test_name-2", affiliation: [{ "name": "2", "affiliationIdentifier": "https://ror.org/2", @@ -1594,6 +1602,7 @@ create(:doi, aasm_state: "findable", types: { "resourceTypeGeneral" => "Preprint" }, creators: [{ + name: "test_name-1", affiliation: [ { "name": "1", @@ -1614,7 +1623,9 @@ end let!(:missing) do create_list(:doi, 3, aasm_state: "findable", - creators: [{ affiliation: [] }], + creators: [{ + name: "test_name-3", + affiliation: [] }], rights_list: []) end @@ -1654,9 +1665,9 @@ { "name" => "Kristian Garza", "nameType" => "Personal", - "affiliation" => { + "affiliation" => [{ "name" => "Ruhr-University Bochum, Germany" - } + }] }, { "familyName" => "Garza", @@ -1692,18 +1703,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" } ] diff --git a/spec/models/doi_spec.rb b/spec/models/doi_spec.rb index 02aa3612c..dda4ec620 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 @@ -1430,7 +1430,7 @@ publisher: publisher, publication_year: publication_year, types: types, - descriptions: [{ "description" => description }], + descriptions: [{ "description" => description, "descriptionType" => "Abstract" }], event: "publish") end @@ -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/patch_spec.rb b/spec/requests/datacite_dois/patch_spec.rb index b3d729cff..23b7e8c9b 100644 --- a/spec/requests/datacite_dois/patch_spec.rb +++ b/spec/requests/datacite_dois/patch_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") } @@ -417,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" => "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" => "Value at root is not an array", "uid" => "10.14454/4k3m-nyvg" }]) end end 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" => [ diff --git a/spec/requests/datacite_dois/post_spec.rb b/spec/requests/datacite_dois/post_spec.rb index 0eeab9d71..f0a32fcfb 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" => { @@ -826,6 +826,7 @@ "nameType": "Personal", "givenName": "Julia M.", "familyName": "Rovera", + "name": "Rovera, Julia M.", "affiliation": [{ "name": "Drexel University" }], @@ -841,8 +842,7 @@ "titles": [ { "lang": "en", - "title": "The Relationship Among Sport Type, Micronutrient Intake and Bone Mineral Density in an Athlete Population", - "titleType": null + "title": "The Relationship Among Sport Type, Micronutrient Intake and Bone Mineral Density in an Athlete Population" }, { "lang": "en", @@ -890,7 +890,7 @@ expect(json.dig("data", "attributes", "doi")).to eq("10.14454/9zwb-rb91") expect(json.dig("data", "attributes", "types")).to eq("bibtex" => "phdthesis", "citeproc" => "thesis", "resourceType" => "Dissertation", "resourceTypeGeneral" => "Text", "ris" => "THES", "schemaOrg" => "Thesis") expect(json.dig("data", "attributes", "descriptions", 0, "description")).to start_with("Diet and physical activity") - expect(json.dig("data", "attributes", "titles")).to eq([{ "lang" => "en", "title" => "The Relationship Among Sport Type, Micronutrient Intake and Bone Mineral Density in an Athlete Population", "titleType" => nil }, { "lang" => "en", "title" => "Subtitle", "titleType" => "Subtitle" }]) + expect(json.dig("data", "attributes", "titles")).to eq([{ "lang" => "en", "title" => "The Relationship Among Sport Type, Micronutrient Intake and Bone Mineral Density in an Athlete Population" }, { "lang" => "en", "title" => "Subtitle", "titleType" => "Subtitle" }]) expect(json.dig("data", "attributes", "state")).to eq("findable") xml = Maremma.from_xml(Base64.decode64(json.dig("data", "attributes", "xml"))).fetch("resource", {}) @@ -906,10 +906,11 @@ 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 + # 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) } @@ -1287,7 +1288,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 { @@ -2192,7 +2193,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 @@ -2622,4 +2623,97 @@ 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 - VALID doi" do + before do + valid_attributes["data"]["attributes"]["language"] = "fr" + end + + it "creates a Doi" do + 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 + VCR.turned_off do + post "/dois", valid_attributes, headers + end + + 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