diff --git a/app/models/doi.rb b/app/models/doi.rb index e69ec7eec..ba9900cff 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -79,7 +79,7 @@ class Doi < ActiveRecord::Base validates_format_of :url, :with => /\A(ftp|http|https):\/\/[\S]+/ , if: :url?, message: "URL is not valid" validates_uniqueness_of :doi, message: "This DOI has already been taken", unless: :only_validate validates :last_landing_page_status, numericality: { only_integer: true }, if: :last_landing_page_status? - validates :xml, presence: true, xml_schema: true, :if => Proc.new { |doi| doi.validatable? } + validates :xml, xml_schema: true, :if => Proc.new { |doi| doi.validatable? } after_commit :update_url, on: [:create, :update] after_commit :update_media, on: [:create, :update] @@ -350,7 +350,7 @@ def self.import_one(doi_id: nil) logger.error "[MySQL] No metadata for DOI " + doi.doi + " found: " + doi.current_metadata.inspect return nil end - + meta = doi.read_datacite(string: string, sandbox: doi.sandbox) attrs = %w(creators contributors titles publisher publication_year types descriptions container sizes formats language dates identifiers related_identifiers funding_references geo_locations rights_list subjects content_url).map do |a| [a.to_sym, meta[a]] @@ -402,7 +402,7 @@ def self.import_by_day(options={}) logger.error "[MySQL] No metadata for DOI " + doi.doi + " found." return nil end - + meta = doi.read_datacite(string: string, sandbox: doi.sandbox) attrs = %w(creators contributors titles publisher publication_year types descriptions container sizes formats language dates identifiers related_identifiers funding_references geo_locations rights_list subjects content_url).map do |a| [a.to_sym, meta[a]] @@ -438,7 +438,7 @@ def self.import_by_day_missing(options={}) logger.error "[MySQL] No metadata for DOI " + doi.doi + " found." return nil end - + meta = doi.read_datacite(string: string, sandbox: doi.sandbox) attrs = %w(creators contributors titles publisher publication_year types descriptions container sizes formats language dates identifiers related_identifiers funding_references geo_locations rights_list subjects content_url).map do |a| [a.to_sym, meta[a]] @@ -661,7 +661,7 @@ def validatable? # providers europ and ethz do their own handle registration, so fetch url from handle system instead def update_url return nil if current_user.nil? || !is_registered_or_findable? - + if %w(europ ethz).include?(provider_id) || %w(Crossref).include?(agency) UrlJob.perform_later(self) else diff --git a/lib/xml_schema_validator.rb b/lib/xml_schema_validator.rb index 37272c68f..243ee9164 100644 --- a/lib/xml_schema_validator.rb +++ b/lib/xml_schema_validator.rb @@ -32,18 +32,26 @@ def get_valid_kernel(sv) end def validate_each(record, attribute, value) + unless value.present? + record.errors[:xml] << "xml should be present" + return false + end + kernel = get_valid_kernel(record.schema_version) - return false unless kernel.present? - + unless kernel.present? + record.errors[:xml] << "schema should be present" + return false + end + filepath = Bundler.rubygems.find_name('bolognese').first.full_gem_path + "/resources/#{kernel}/metadata.xsd" schema = Nokogiri::XML::Schema(open(filepath)) - + schema.validate(Nokogiri::XML(value, nil, 'UTF-8')).reduce({}) do |sum, error| location, level, source, text = error.message.split(": ", 4) line, column = location.split(":", 2) title = text.to_s.strip + " at line #{line}, column #{column}" if line.present? source = source.split("}").last[0..-2] if line.present? - source = schema_attributes(source) if source.present? + source = schema_attributes(source) if source.present? record.errors[source.to_sym] << title end rescue Nokogiri::XML::SyntaxError => e diff --git a/spec/requests/dois_spec.rb b/spec/requests/dois_spec.rb index ac72c056b..a86457aa4 100644 --- a/spec/requests/dois_spec.rb +++ b/spec/requests/dois_spec.rb @@ -718,6 +718,44 @@ expect(json.dig('data', 'attributes', 'state')).to eq("findable") end end + + context 'when updating landing page attributes' do + let(:doi) { create(:doi, doi: "10.14454/10705", url: "http://www.bl.uk/pdf/pat.pdf", client: client, aasm_state: "findable") } + + let(:landingPage) { { + "checked" => Time.zone.now.utc.iso8601, + "status" => 200, + "url" => doi.url, + "contentType" => "text/html", + "error" => nil, + "redirectCount" => 0, + "redirectUrls" => [], + "downloadLatency" => 200, + "hasSchemaOrg" => true, + "schemaOrgId" => doi.doi, + "dcIdentifier" => nil, + "citationDoi" => nil, + "bodyHasPid" => true + } } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "landingPage" => landingPage, + } + } + } + end + + before { patch "/dois/#{doi.doi}", params: valid_attributes.to_json, headers: headers } + + it 'updating landingPage data' do + expect(response).to have_http_status(200) + expect(json.dig('data', 'attributes', 'landingPage')).to eq(landingPage) + end + end + end describe 'POST /dois' do @@ -754,7 +792,7 @@ expect(json.dig('data', 'attributes', 'schemaVersion')).to eq("http://datacite.org/schema/kernel-4") 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") - + doc = Nokogiri::XML(Base64.decode64(json.dig('data', 'attributes', 'xml')), nil, 'UTF-8', &:noblanks) expect(doc.at_css("identifier").content).to eq("10.14454/10703") end @@ -2240,364 +2278,364 @@ let(:client) { create(:client, provider: provider, symbol: ENV['MDS_USERNAME'], password: ENV['MDS_PASSWORD']) } let(:bearer) { Client.generate_token(role_id: "client_admin", uid: client.symbol, provider_id: provider.symbol.downcase, client_id: client.symbol.downcase, password: client.password) } let(:doi) { create(:doi, client: client, aasm_state: "findable") } - + context "no permission" do let(:doi) { create(:doi) } - + before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.jats+xml", 'Authorization' => 'Bearer ' + bearer } } - + it 'returns error message' do expect(json["errors"]).to eq([{"status"=>"403", "title"=>"You are not authorized to access this resource."}]) end - + it 'returns status code 403' do expect(response).to have_http_status(403) end end - + context "no authentication" do - let(:doi) { create(:doi) } + let(:doi) { create(:doi) } before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.jats+xml" } } - + it 'returns error message' do expect(json["errors"]).to eq([{"status"=>"401", "title"=>"Bad credentials."}]) end - + it 'returns status code 401' do expect(response).to have_http_status(401) end end - + context "application/vnd.jats+xml" do before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.jats+xml", 'Authorization' => 'Bearer ' + bearer } } - + it 'returns the Doi' do jats = Maremma.from_xml(response.body).fetch("element_citation", {}) expect(jats.dig("publication_type")).to eq("data") expect(jats.dig("data_title")).to eq("Data from: A new malaria agent in African hominids.") end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "application/vnd.jats+xml link" do before { get "/dois/application/vnd.jats+xml/#{doi.doi}" } - + it 'returns the Doi' do jats = Maremma.from_xml(response.body).fetch("element_citation", {}) expect(jats.dig("publication_type")).to eq("data") expect(jats.dig("data_title")).to eq("Data from: A new malaria agent in African hominids.") end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "application/vnd.datacite.datacite+xml" do before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+xml", 'Authorization' => 'Bearer ' + bearer } } - + it 'returns the Doi' do data = Maremma.from_xml(response.body).to_h.fetch("resource", {}) expect(data.dig("xmlns")).to eq("http://datacite.org/schema/kernel-4") expect(data.dig("publisher")).to eq("Dryad Digital Repository") expect(data.dig("titles", "title")).to eq("Data from: A new malaria agent in African hominids.") end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "application/vnd.datacite.datacite+xml link" do before { get "/dois/application/vnd.datacite.datacite+xml/#{doi.doi}" } - + it 'returns the Doi' do data = Maremma.from_xml(response.body).to_h.fetch("resource", {}) expect(data.dig("xmlns")).to eq("http://datacite.org/schema/kernel-4") expect(data.dig("publisher")).to eq("Dryad Digital Repository") expect(data.dig("titles", "title")).to eq("Data from: A new malaria agent in African hominids.") end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "application/vnd.datacite.datacite+xml schema 3" do let(:xml) { file_fixture('datacite_schema_3.xml').read } let(:doi) { create(:doi, xml: xml, client: client, regenerate: false) } - + before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+xml", 'Authorization' => 'Bearer ' + bearer } } - + it 'returns the Doi' do data = Maremma.from_xml(response.body).to_h.fetch("resource", {}) expect(data.dig("xmlns")).to eq("http://datacite.org/schema/kernel-3") expect(data.dig("publisher")).to eq("Dryad Digital Repository") expect(data.dig("titles", "title")).to eq("Data from: A new malaria agent in African hominids.") end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + # context "no metadata" do # let(:doi) { create(:doi, xml: nil, client: client) } - + # before { get "/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+xml", 'Authorization' => 'Bearer ' + bearer } } - + # it 'returns the Doi' do # expect(response.body).to eq('') # end - + # it 'returns status code 200' do # expect(response).to have_http_status(200) # end # end - + context "application/vnd.datacite.datacite+xml not found" do before { get "/dois/xxx", headers: { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+xml", 'Authorization' => 'Bearer ' + bearer } } - + it 'returns error message' do expect(json["errors"]).to eq([{"status"=>"404", "title"=>"The resource you are looking for doesn't exist."}]) end - + it 'returns status code 404' do expect(response).to have_http_status(404) end end - + context "application/vnd.datacite.datacite+json" do before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+json", 'Authorization' => 'Bearer ' + bearer } } - + it 'returns the Doi' do expect(json["doi"]).to eq(doi.doi) end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "application/vnd.datacite.datacite+json link" do before { get "/dois/application/vnd.datacite.datacite+json/#{doi.doi}" } - + it 'returns the Doi' do expect(json["doi"]).to eq(doi.doi) end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "application/vnd.crosscite.crosscite+json" do before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.crosscite.crosscite+json", 'Authorization' => 'Bearer ' + bearer } } - + it 'returns the Doi' do expect(json["doi"]).to eq(doi.doi) end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "application/vnd.crosscite.crosscite+json link" do before { get "/dois/application/vnd.crosscite.crosscite+json/#{doi.doi}" } - + it 'returns the Doi' do expect(json["doi"]).to eq(doi.doi) end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "application/vnd.schemaorg.ld+json" do before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.schemaorg.ld+json", 'Authorization' => 'Bearer ' + bearer } } - + it 'returns the Doi' do expect(json["@type"]).to eq("Dataset") end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "application/vnd.schemaorg.ld+json link" do before { get "/dois/application/vnd.schemaorg.ld+json/#{doi.doi}" } - + it 'returns the Doi' do expect(json["@type"]).to eq("Dataset") end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "application/vnd.citationstyles.csl+json" do before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/vnd.citationstyles.csl+json", 'Authorization' => 'Bearer ' + bearer } } - + it 'returns the Doi' do expect(json["type"]).to eq("dataset") end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "application/vnd.citationstyles.csl+json link" do before { get "/dois/application/vnd.citationstyles.csl+json/#{doi.doi}" } - + it 'returns the Doi' do expect(json["type"]).to eq("dataset") end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "application/x-research-info-systems" do before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/x-research-info-systems", 'Authorization' => 'Bearer ' + bearer } } - + it 'returns the Doi' do expect(response.body).to start_with("TY - DATA") end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "application/x-research-info-systems link" do before { get "/dois/application/x-research-info-systems/#{doi.doi}" } - + it 'returns the Doi' do expect(response.body).to start_with("TY - DATA") end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "application/x-bibtex" do before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "application/x-bibtex", 'Authorization' => 'Bearer ' + bearer } } - + it 'returns the Doi' do expect(response.body).to start_with("@misc{https://doi.org/#{doi.doi.downcase}") end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "application/x-bibtex link" do before { get "/dois/application/x-bibtex/#{doi.doi}" } - + it 'returns the Doi' do expect(response.body).to start_with("@misc{https://doi.org/#{doi.doi.downcase}") end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "text/x-bibliography", vcr: true do context "default style" do before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "text/x-bibliography", 'Authorization' => 'Bearer ' + bearer } } - + it 'returns the Doi' do expect(response.body).to start_with("Ollomo, B.") end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "default style link" do before { get "/dois/text/x-bibliography/#{doi.doi}" } - + it 'returns the Doi' do expect(response.body).to start_with("Ollomo, B.") end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "ieee style" do before { get "/dois/#{doi.doi}?style=ieee", headers: { "HTTP_ACCEPT" => "text/x-bibliography", 'Authorization' => 'Bearer ' + bearer } } - + it 'returns the Doi' do expect(response.body).to start_with("B. Ollomo") end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - - context "ieee style link" do + + context "ieee style link" do before { get "/dois/text/x-bibliography/#{doi.doi}?style=ieee" } - + it 'returns the Doi' do expect(response.body).to start_with("B. Ollomo") end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - + context "style and locale" do before { get "/dois/#{doi.doi}?style=vancouver&locale=de", headers: { "HTTP_ACCEPT" => "text/x-bibliography", 'Authorization' => 'Bearer ' + bearer } } - + it 'returns the Doi' do expect(response.body).to start_with("Ollomo B") end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end end - + context "unknown content type" do before { get "/dois/#{doi.doi}", headers: { "HTTP_ACCEPT" => "text/csv", 'Authorization' => 'Bearer ' + bearer } } - + it 'returns the Doi' do expect(json["errors"]).to eq([{"status"=>"406", "title"=>"The content type is not recognized."}]) end - + it 'returns status code 406' do expect(response).to have_http_status(406) end end - + context "missing content type" do before { get "/dois/#{doi.doi}", headers: { 'Authorization' => 'Bearer ' + bearer } } - + it 'returns the Doi' do expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) end - + it 'returns status code 200' do expect(response).to have_http_status(200) end end - end + end end