From 342dbcbb87d0ab736b88d550e8e24d130e901d3a Mon Sep 17 00:00:00 2001 From: Martin Fenner Date: Tue, 9 Mar 2021 09:36:47 +0100 Subject: [PATCH 1/3] adding specs for v3 api requests --- app/controllers/v3/events_controller.rb | 16 +- app/controllers/v3/sessions_controller.rb | 86 - config/routes.rb | 1 + spec/factories/default.rb | 2 +- spec/requests/contacts_spec.rb | 2 +- spec/requests/v3/activities_spec.rb | 181 + spec/requests/v3/contacts_spec.rb | 385 ++ spec/requests/v3/datacite_dois_spec.rb | 4108 ++++++++++++++++++ spec/requests/v3/events_spec.rb | 1126 +++++ spec/requests/v3/exports_spec.rb | 175 + spec/requests/v3/heartbeat_spec.rb | 12 + spec/requests/v3/index_spec.rb | 342 ++ spec/requests/v3/media_spec.rb | 283 ++ spec/requests/v3/metadata_spec.rb | 230 + spec/requests/v3/prefixes_spec.rb | 142 + spec/requests/v3/provider_prefixes_spec.rb | 234 + spec/requests/v3/providers_spec.rb | 1167 +++++ spec/requests/v3/random_spec.rb | 22 + spec/requests/v3/repositories_spec.rb | 630 +++ spec/requests/v3/repository_prefixes_spec.rb | 194 + 20 files changed, 9244 insertions(+), 94 deletions(-) delete mode 100644 app/controllers/v3/sessions_controller.rb create mode 100644 spec/requests/v3/activities_spec.rb create mode 100644 spec/requests/v3/contacts_spec.rb create mode 100644 spec/requests/v3/datacite_dois_spec.rb create mode 100644 spec/requests/v3/events_spec.rb create mode 100644 spec/requests/v3/exports_spec.rb create mode 100644 spec/requests/v3/heartbeat_spec.rb create mode 100644 spec/requests/v3/index_spec.rb create mode 100644 spec/requests/v3/media_spec.rb create mode 100644 spec/requests/v3/metadata_spec.rb create mode 100644 spec/requests/v3/prefixes_spec.rb create mode 100644 spec/requests/v3/provider_prefixes_spec.rb create mode 100644 spec/requests/v3/providers_spec.rb create mode 100644 spec/requests/v3/random_spec.rb create mode 100644 spec/requests/v3/repositories_spec.rb create mode 100644 spec/requests/v3/repository_prefixes_spec.rb diff --git a/app/controllers/v3/events_controller.rb b/app/controllers/v3/events_controller.rb index f593cba5f..e3131888c 100644 --- a/app/controllers/v3/events_controller.rb +++ b/app/controllers/v3/events_controller.rb @@ -132,10 +132,10 @@ def index publication_year: params[:publication_year], occurred_at: params[:occurred_at], year_month: params[:year_month], - aggregations: params[:aggregations], unique: params[:unique], state_event: params[:state], scroll_id: params[:scroll_id], + facet: params[:facet], page: page, sort: sort, ) @@ -177,28 +177,32 @@ def index render json: V3::EventSerializer.new(results, options).serialized_json, status: :ok else + # parse "facet" query parameter to decide which facets should be shown + # by default no facets are shown + facets = Array.wrap(params[:facet].to_s.split(",")) + sources = - if total.positive? + if total.positive? && facets.include?("sources") facet_by_source(response.response.aggregations.sources.buckets) end prefixes = - if total.positive? + if total.positive? && facets.include?("prefixes") facet_by_source(response.response.aggregations.prefixes.buckets) end citation_types = - if total.positive? + if total.positive? && facets.include?("citation_types") facet_by_citation_type( response.response.aggregations.citation_types.buckets, ) end relation_types = - if total.positive? + if total.positive? && facets.include?("relation_types") facet_by_relation_type( response.response.aggregations.relation_types.buckets, ) end registrants = - if total.positive? + if total.positive? && facets.include?("registrants") facet_by_registrants( response.response.aggregations.registrants.buckets, ) diff --git a/app/controllers/v3/sessions_controller.rb b/app/controllers/v3/sessions_controller.rb deleted file mode 100644 index 2d7c8d6fa..000000000 --- a/app/controllers/v3/sessions_controller.rb +++ /dev/null @@ -1,86 +0,0 @@ -# frozen_string_literal: true - -class V3::SessionsController < ApplicationController - def create_token - if safe_params[:grant_type] != "password" - error_response("Wrong grant type.") && return - end - if safe_params[:username].blank? || safe_params[:username] == "undefined" || - safe_params[:password].blank? || - safe_params[:password] == "undefined" - error_response("Missing account ID or password.") && return - end - - credentials = - User.encode_auth_param( - username: safe_params[:username], password: safe_params[:password], - ) - user = User.new(credentials, type: "basic") - - error_response(user.errors) && return if user.errors.present? - if user.role_id == "anonymous" - error_response("Wrong account ID or password.") && return - end - - render json: { - "access_token" => user.jwt, "expires_in" => 3_600 * 24 * 30 - }.to_json, - status: :ok - end - - def create_oidc_token - if safe_params[:token].blank? || safe_params[:token] == "undefined" - error_response("Missing token.") && return - end - - user = User.new(safe_params[:token], type: "oidc") - error_response(user.errors) && return if user.errors.present? - - render json: { - "access_token" => user.jwt, "expires_in" => 3_600 * 24 * 30 - }.to_json, - status: :ok - end - - def reset - if safe_params[:username].blank? - message = "Missing account ID." - status = :ok - else - response = User.reset(safe_params[:username]) - if response.present? - message = response[:message] - status = response[:status] - else - message = "Account not found." - status = :ok - end - end - - render json: { "message" => message }.to_json, status: status - end - - private - def error_response(message) - status = 400 - logger.error message - render json: { errors: [{ status: status.to_s, title: message }] }.to_json, - status: status - end - - def safe_params - params.permit( - :grant_type, - :username, - :password, - :token, - :client_id, - :client_secret, - :refresh_token, - :session, - :format, - :controller, - :action, - ) - end -end diff --git a/config/routes.rb b/config/routes.rb index 1dd4cdb05..86e5e4dd1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -246,6 +246,7 @@ resources :activities resources :events end + resources :events resources :prefixes, constraints: { id: /.+/ } resources :provider_prefixes, path: "provider-prefixes" resources :random, only: %i[index] diff --git a/spec/factories/default.rb b/spec/factories/default.rb index d0e60e4db..2a46e0dfd 100644 --- a/spec/factories/default.rb +++ b/spec/factories/default.rb @@ -73,7 +73,7 @@ provider uid { SecureRandom.uuid } - email { "josiah@example.org" } + sequence(:email) { |n| "josiah#{n}@example.org" } given_name { "Josiah" } family_name { "Carberry" } role_name { ["voting"] } diff --git a/spec/requests/contacts_spec.rb b/spec/requests/contacts_spec.rb index 5e4f30e10..0dc7fe663 100644 --- a/spec/requests/contacts_spec.rb +++ b/spec/requests/contacts_spec.rb @@ -189,7 +189,7 @@ attributes = json.dig("data", 1, "attributes") expect(attributes["name"]).to eq("Josiah Carberry") - expect(attributes["email"]).to eq("josiah@example.org") + expect(attributes["email"]).to start_with("josiah") expect(attributes["roleName"]).to eq(["billing"]) relationships = json.dig("data", 1, "relationships") diff --git a/spec/requests/v3/activities_spec.rb b/spec/requests/v3/activities_spec.rb new file mode 100644 index 000000000..77c99da1d --- /dev/null +++ b/spec/requests/v3/activities_spec.rb @@ -0,0 +1,181 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe ActivitiesController, type: :request do + let(:provider) { create(:provider, symbol: "DATACITE") } + let(:client) do + create( + :client, + provider: provider, + symbol: ENV["MDS_USERNAME"], + password: ENV["MDS_PASSWORD"], + ) + end + let(:doi) { create(:doi, client: client) } + let(:bearer) do + Client.generate_token( + role_id: "client_admin", + uid: client.symbol, + provider_id: provider.symbol.downcase, + client_id: client.symbol.downcase, + password: client.password, + ) + end + let(:headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => "Bearer " + bearer, + } + end + + describe "activities for doi", elasticsearch: true do + let!(:doi) { create(:doi, client: client) } + let!(:other_doi) { create(:doi, client: client) } + + before do + DataciteDoi.import + Activity.import + sleep 2 + end + + context "without username" do + it "returns the activities" do + get "/v3/dois/#{doi.doi.downcase}/activities", + nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data").length).to eq(1) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("data", 0, "attributes", "action")).to eq("create") + expect( + json.dig("data", 0, "attributes", "changes", "aasm_state"), + ).to eq("draft") + + expect( + json.dig("data", 0, "attributes", "prov:wasAttributedTo"), + ).to be_nil + expect( + json.dig("data", 0, "attributes", "prov:wasGeneratedBy"), + ).to be_present + expect( + json.dig("data", 0, "attributes", "prov:generatedAtTime"), + ).to be_present + expect( + json.dig("data", 0, "attributes", "prov:wasDerivedFrom"), + ).to be_present + end + end + end + + describe "activities for repository", elasticsearch: true do + let!(:doi) { create(:doi, client: client) } + let!(:other_doi) { create(:doi, client: client) } + + before do + DataciteDoi.import + Activity.import + sleep 2 + end + + context "repository" do + it "returns the activities" do + get "/v3/repositories/#{client.symbol.downcase}/activities", + nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data").length).to eq(1) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("data", 0, "attributes", "action")).to eq("create") + + expect( + json.dig("data", 0, "attributes", "prov:wasAttributedTo"), + ).to be_nil + expect( + json.dig("data", 0, "attributes", "prov:wasGeneratedBy"), + ).to be_present + expect( + json.dig("data", 0, "attributes", "prov:generatedAtTime"), + ).to be_present + expect( + json.dig("data", 0, "attributes", "prov:wasDerivedFrom"), + ).to be_present + end + end + end + + describe "activities for provider", elasticsearch: true do + let!(:doi) { create(:doi, client: client) } + let!(:other_doi) { create(:doi, client: client) } + + before do + DataciteDoi.import + Activity.import + sleep 2 + end + + context "provider" do + it "returns the activities" do + get "/v3/providers/#{provider.symbol.downcase}/activities", + nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data").length).to eq(1) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("data", 0, "attributes", "action")).to eq("create") + + expect( + json.dig("data", 0, "attributes", "prov:wasAttributedTo"), + ).to be_nil + expect( + json.dig("data", 0, "attributes", "prov:wasGeneratedBy"), + ).to be_present + expect( + json.dig("data", 0, "attributes", "prov:generatedAtTime"), + ).to be_present + expect( + json.dig("data", 0, "attributes", "prov:wasDerivedFrom"), + ).to be_present + end + end + end + + describe "query activities", elasticsearch: true do + let!(:doi) { create(:doi, client: client) } + let!(:other_doi) { create(:doi, client: client) } + + before do + DataciteDoi.import + Activity.import + sleep 2 + end + + context "query by doi" do + it "returns the activities" do + get "/v3/activities?datacite-doi-id=#{doi.doi.downcase}", + nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data").length).to eq(1) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("data", 0, "attributes", "action")).to eq("create") + expect( + json.dig("data", 0, "attributes", "changes", "aasm_state"), + ).to eq("draft") + + expect( + json.dig("data", 0, "attributes", "prov:wasAttributedTo"), + ).to be_nil + expect( + json.dig("data", 0, "attributes", "prov:wasGeneratedBy"), + ).to be_present + expect( + json.dig("data", 0, "attributes", "prov:generatedAtTime"), + ).to be_present + expect( + json.dig("data", 0, "attributes", "prov:wasDerivedFrom"), + ).to be_present + end + end + end +end diff --git a/spec/requests/v3/contacts_spec.rb b/spec/requests/v3/contacts_spec.rb new file mode 100644 index 000000000..d2969b5d7 --- /dev/null +++ b/spec/requests/v3/contacts_spec.rb @@ -0,0 +1,385 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe ContactsController, type: :request, elasticsearch: true do + let(:bearer) { User.generate_token } + let(:consortium) { create(:provider, name: "DC", role_name: "ROLE_CONSORTIUM") } + let(:provider) do + create( + :provider, + consortium: consortium, role_name: "ROLE_CONSORTIUM_ORGANIZATION", + ) + end + let!(:service_contact) { create(:contact, provider: provider, role_name: ["service"]) } + let!(:contact) { create(:contact, provider: provider, role_name: ["billing"]) } + let(:params) do + { + "data" => { + "type" => "contacts", + "attributes" => { + "givenName" => "Josiah", + "familyName" => "Carberry", + "email" => "bob@example.com", + "roleName" => ["voting"] + }, + "relationships": { + "provider": { + "data": { "type": "providers", "id": provider.uid }, + } + }, + }, + } + end + let(:headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => "Bearer " + bearer, + } + end + + describe "GET /v3/contacts", elasticsearch: true do + let!(:contacts) { create_list(:contact, 3) } + + before do + Contact.import + sleep 1 + end + + it "returns contacts" do + get "/v3/contacts", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(5) + expect(json.dig("meta")).to eq("page" => 1, "roles" => [{ "count" => 3, "id" => "voting", "title" => "Voting" }, { "count" => 1, "id" => "billing", "title" => "Billing" }, { "count" => 1, "id" => "service", "title" => "Service" }], "total" => 5, "totalPages" => 1) + end + end + + describe "GET /v3/contacts query" do + let!(:contacts) { create_list(:contact, 3) } + + before do + Contact.import + sleep 1 + end + + it "returns contacts" do + get "/v3/contacts?query=carberry", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(5) + expect(json.dig("meta", "total")).to eq(5) + end + end + + describe "GET /v3/contacts query role_name" do + let!(:contacts) { create_list(:contact, 3) } + + before do + Contact.import + sleep 1 + end + + it "returns contacts" do + get "/v3/contacts?role-name=billing", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(1) + expect(json.dig("meta", "total")).to eq(1) + end + end + + describe "GET /v3/contacts by provider" do + let!(:contacts) { create_list(:contact, 3, provider: provider) } + + before do + Provider.import + Contact.import + sleep 1 + end + + it "returns contacts" do + get "/v3/contacts?provider-id=#{provider.uid}", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(5) + expect(json.dig("meta", "total")).to eq(5) + end + end + + describe "GET /v3/contacts by consortium" do + let!(:consortium_contact) { create(:contact, provider: consortium, role_name: ["billing"]) } + + before do + Provider.import + Contact.import + sleep 1 + end + + it "returns contacts" do + get "/v3/contacts?consortium-id=#{consortium.uid}", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(3) + expect(json.dig("meta", "total")).to eq(3) + end + end + + describe "GET /v3/contacts exclude deleted" do + let!(:contacts) { create_list(:contact, 3, deleted_at: Time.zone.now) } + + before do + Contact.import + sleep 1 + end + + it "returns contacts" do + get "/v3/contacts", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(2) + expect(json.dig("meta", "total")).to eq(2) + end + end + + describe "GET /v3/contacts/:id" do + context "when the record exists" do + it "returns the contact" do + get "/v3/contacts/#{contact.uid}", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "name")).to eq(contact.name) + end + end + + context "when the record does not exist" do + it "returns status code 404" do + get "/v3/contacts/xxx", nil, headers + + expect(last_response.status).to eq(404) + expect(json["errors"].first).to eq( + "status" => "404", + "title" => "The resource you are looking for doesn't exist.", + ) + end + end + end + + describe "POST /v3/contacts" do + context "when the request is valid" do + it "creates a contact" do + post "/v3/contacts", params, headers + + expect(last_response.status).to eq(201) + attributes = json.dig("data", "attributes") + expect(attributes["name"]).to eq("Josiah Carberry") + expect(attributes["email"]).to eq("bob@example.com") + expect(attributes["roleName"]).to eq(["voting"]) + + relationships = json.dig("data", "relationships") + expect(relationships).to eq("provider" => { "data" => { "id" => provider.uid, "type" => "providers" } }) + + Contact.import + sleep 2 + + get "/v3/contacts", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data").length).to eq(3) + + attributes = json.dig("data", 1, "attributes") + expect(attributes["name"]).to eq("Josiah Carberry") + expect(attributes["email"]).to start_with("josiah") + expect(attributes["roleName"]).to eq(["billing"]) + + relationships = json.dig("data", 1, "relationships") + expect(relationships.dig("provider", "data", "id")).to eq( + provider.uid, + ) + end + end + + context "when the request is invalid" do + let(:params) do + { + "data" => { + "type" => "contacts", + "attributes" => { + "givenName" => "Josiah", + "familyName" => "Carberry", + }, + "relationships": { + "provider": { + "data": { "type": "providers", "id": provider.uid }, + }, + }, + }, + } + end + + it "returns a validation failure message" do + post "/v3/contacts", params, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq( + [ + { "source" => "email", "title" => "Can't be blank" }, + ], + ) + end + end + end + + describe "PUT /v3/contacts/:id" do + context "when the record exists" do + let(:params) do + { + "data" => { + "type" => "contacts", + "attributes" => { + "familyName" => "Smith", + }, + }, + } + end + + it "updates the record" do + put "/v3/contacts/#{contact.uid}", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "familyName")).to eq( + "Smith", + ) + expect(json.dig("data", "attributes", "name")).to eq( + "Josiah Smith", + ) + end + end + + context "updates role_name" do + let(:params) do + { + "data" => { + "type" => "contacts", + "attributes" => { + "roleName" => ["technical", "voting"], + }, + }, + } + end + + it "updates the record" do + put "/v3/contacts/#{contact.uid}", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "roleName")).to eq( + ["technical", "voting"], + ) + end + end + + context "updates role_name invalid" do + let(:params) do + { + "data" => { + "type" => "contacts", + "attributes" => { + "roleName" => ["catering"], + }, + }, + } + end + + it "updates the record" do + put "/v3/contacts/#{contact.uid}", params, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq([{ "source" => "role_name", "title" => "Role name 'catering' is not included in the list of possible role names.", "uid" => contact.uid }]) + end + end + + context "updates role_name already taken" do + let(:params) do + { + "data" => { + "type" => "contacts", + "attributes" => { + "roleName" => ["service"], + }, + }, + } + end + + it "updates the record" do + put "/v3/contacts/#{contact.uid}", params, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq([{ "source" => "role_name", "title" => "Role name 'service' is already taken.", "uid" => contact.uid }]) + end + end + + context "removes given name and family name" do + let(:params) do + { + "data" => { + "type" => "contacts", "attributes" => { "givenName" => nil, "familyName" => nil } + }, + } + end + + it "updates the record" do + put "/v3/contacts/#{contact.uid}", params, headers + puts last_response.body + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "givenName")).to be_nil + expect(json.dig("data", "attributes", "familyName")).to be_nil + expect(json.dig("data", "attributes", "name")).to be_nil + end + end + + context "invalid email" do + let(:params) do + { + "data" => { + "type" => "contacts", "attributes" => { "email" => "abc" } + }, + } + end + + it "updates the record" do + put "/v3/contacts/#{contact.uid}", params, headers + + expect(last_response.status).to eq(422) + expect(json["errors"].first).to eq( + "source" => "email", "title" => "Email should be valid", + "uid" => contact.uid + ) + end + end + end + + describe "DELETE /v3/contacts/:id" do + it "returns status code 204" do + delete "/v3/contacts/#{contact.uid}", nil, headers + + expect(last_response.status).to eq(204) + end + + context "when the resource doesnt exist" do + it "returns status code 404" do + delete "/v3/contacts/xxx", nil, headers + + expect(last_response.status).to eq(404) + end + + it "returns a validation failure message" do + delete "/v3/contacts/xxx", nil, headers + + expect(json["errors"].first).to eq( + "status" => "404", + "title" => "The resource you are looking for doesn't exist.", + ) + end + end + end +end diff --git a/spec/requests/v3/datacite_dois_spec.rb b/spec/requests/v3/datacite_dois_spec.rb new file mode 100644 index 000000000..31161640e --- /dev/null +++ b/spec/requests/v3/datacite_dois_spec.rb @@ -0,0 +1,4108 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe DataciteDoisController, type: :request, vcr: true do + let(:admin) { create(:provider, symbol: "ADMIN") } + let(:admin_bearer) { Client.generate_token(role_id: "staff_admin", uid: admin.symbol, password: admin.password) } + let(:admin_headers) { { "HTTP_ACCEPT" => "application/vnd.api+json", "HTTP_AUTHORIZATION" => "Bearer " + admin_bearer } } + + let(:provider) { create(:provider, symbol: "DATACITE") } + let(:client) { create(:client, provider: provider, symbol: ENV["MDS_USERNAME"], password: ENV["MDS_PASSWORD"], re3data_id: "10.17616/r3xs37") } + let!(:prefix) { create(:prefix, uid: "10.14454") } + let!(:client_prefix) { create(:client_prefix, client: client, prefix: prefix) } + + let(:doi) { create(:doi, client: client, doi: "10.14454/4K3M-NYVG") } + 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(:headers) { { "HTTP_ACCEPT" => "application/vnd.api+json", "HTTP_AUTHORIZATION" => "Bearer " + bearer } } + + describe "GET /v3/dois", elasticsearch: true do + let!(:dois) { create_list(:doi, 10, client: client, aasm_state: "findable", version_info: "testtag") } + + before do + DataciteDoi.import + sleep 2 + @dois = DataciteDoi.query(nil, page: { cursor: [], size: 10 }).results.to_a + end + + it "returns dois" do + get "/v3/dois", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(10) + expect(json.dig("meta", "total")).to eq(10) + end + + it "returns dois with scroll" do + get "/v3/dois?page[scroll]=1m&page[size]=4", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(4) + expect(json.dig("meta", "total")).to eq(10) + expect(json.dig("meta", "scroll-id")).to be_present + next_link_absolute = Addressable::URI.parse(json.dig("links", "next")) + next_link = next_link_absolute.path + "?" + next_link_absolute.query + + get next_link, nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(4) + expect(json.dig("meta", "total")).to eq(10) + expect(json.dig("meta", "scroll-id")).to be_present + next_link_absolute = Addressable::URI.parse(json.dig("links", "next")) + next_link = next_link_absolute.path + "?" + next_link_absolute.query + + get next_link, nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(2) + expect(json.dig("meta", "total")).to eq(10) + expect(json.dig("meta", "scroll-id")).to be_present + expect(json.dig("links", "next")).to be_nil + end + + it "returns dois with offset" do + get "/v3/dois?page[number]=1&page[size]=4", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(4) + expect(json.dig("meta", "total")).to eq(10) + next_link_absolute = Addressable::URI.parse(json.dig("links", "next")) + next_link = next_link_absolute.path + "?" + next_link_absolute.query + expect(next_link).to eq("/v3/dois?page%5Bnumber%5D=2&page%5Bsize%5D=4") + + get next_link, nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(4) + expect(json.dig("meta", "total")).to eq(10) + next_link_absolute = Addressable::URI.parse(json.dig("links", "next")) + next_link = next_link_absolute.path + "?" + next_link_absolute.query + expect(next_link).to eq("/v3/dois?page%5Bnumber%5D=3&page%5Bsize%5D=4") + + get next_link, nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(2) + expect(json.dig("meta", "total")).to eq(10) + expect(json.dig("links", "next")).to be_nil + end + + it "returns dois with cursor" do + get "/v3/dois?page[cursor]&page[size]=4", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(4) + expect(json.dig("data", 3, "id")).to eq(@dois[3].uid) + expect(json.dig("meta", "total")).to eq(10) + cursor = Rack::Utils.parse_query(json.dig("links", "next").split("?", 2).last).fetch("page[cursor]", nil) + expect(Base64.urlsafe_decode64(cursor).split(",").last).to eq(@dois[3].uid) + next_link_absolute = Addressable::URI.parse(json.dig("links", "next")) + next_link = next_link_absolute.path + "?" + next_link_absolute.query + + get next_link, nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(4) + expect(json.dig("data", 3, "id")).to eq(@dois[7].uid) + expect(json.dig("meta", "total")).to eq(10) + cursor = Rack::Utils.parse_query(json.dig("links", "next").split("?", 2).last).fetch("page[cursor]", nil) + expect(Base64.urlsafe_decode64(cursor).split(",").last).to eq(@dois[7].uid) + next_link_absolute = Addressable::URI.parse(json.dig("links", "next")) + next_link = next_link_absolute.path + "?" + next_link_absolute.query + + get next_link, nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(2) + expect(json.dig("data", 1, "id")).to eq(@dois[9].uid) + expect(json.dig("meta", "total")).to eq(10) + expect(json.dig("links", "next")).to be_nil + end + + it "returns dois with version query", vcr: true do + get "/v3/dois?query=version:testtag", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(10) + json["data"].each do |doi| + expect(doi.dig("attributes")).to include("version") + end + end + + it "returns dois with extra detail", vcr: true do + get "/v3/dois?detail=true", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(10) + json["data"].each do |doi| + expect(doi.dig("attributes")).to include("xml") + end + end + + it "returns related provider when detail is enabled", vcr: true do + get "/v3/dois?detail=true", nil, headers + + expect(last_response.status).to eq(200) + json["data"].each do |doi| + expect(doi.dig("relationships", "provider", "data", "id")).to eq(provider.symbol.downcase) + end + end + end + + describe "GET /v3/dois with query", elasticsearch: true do + let!(:doi) do + create(:doi, client: client, aasm_state: "findable", creators: + [{ + "familyName" => "Garza", + "givenName" => "Kristian J.", + "name" => "Garza, Kristian J.", + "nameIdentifiers" => [{ "nameIdentifier" => "https://orcid.org/0000-0003-3484-6875", "nameIdentifierScheme" => "ORCID", "schemeUri" => "https://orcid.org" }], + "nameType" => "Personal", + "affiliation": [ + { + "name": "Freie Universität Berlin", + "affiliationIdentifier": "https://ror.org/046ak2485", + "affiliationIdentifierScheme": "ROR", + }, + ], + }], funding_references: + [{ + "funderIdentifier" => "https://doi.org/10.13039/501100009053", + "funderIdentifierType" => "Crossref Funder ID", + "funderName" => "The Wellcome Trust DBT India Alliance", + }], subjects: + [{ + "subject": "FOS: Computer and information sciences", + "schemeUri": "http://www.oecd.org/science/inno/38235147.pdf", + "subjectScheme": "Fields of Science and Technology (FOS)", + }]) + end + let!(:dois) { create_list(:doi, 3, aasm_state: "findable") } + + before do + DataciteDoi.import + sleep 2 + end + + it "returns dois with short orcid id", vcr: true do + get "/v3/dois?user-id=0000-0003-3484-6875", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("data", 0, "attributes", "creators")).to eq([{ "name" => "Garza, Kristian J.", "nameType" => "Personal", "givenName" => "Kristian J.", "familyName" => "Garza", "affiliation" => ["Freie Universität Berlin"], "nameIdentifiers" => [{ "schemeUri" => "https://orcid.org", "nameIdentifier" => "https://orcid.org/0000-0003-3484-6875", "nameIdentifierScheme" => "ORCID" }] }]) + end + + it "returns dois with orcid id", vcr: true do + get "/v3/dois?user-id=orcid.org/0000-0003-3484-6875", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("data", 0, "attributes", "creators")).to eq([{ "name" => "Garza, Kristian J.", "nameType" => "Personal", "givenName" => "Kristian J.", "familyName" => "Garza", "affiliation" => ["Freie Universität Berlin"], "nameIdentifiers" => [{ "schemeUri" => "https://orcid.org", "nameIdentifier" => "https://orcid.org/0000-0003-3484-6875", "nameIdentifierScheme" => "ORCID" }] }]) + end + + it "returns dois with orcid id as url", vcr: true do + get "/v3/dois?user-id=https://orcid.org/0000-0003-3484-6875", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("data", 0, "attributes", "creators")).to eq([{ "name" => "Garza, Kristian J.", "nameType" => "Personal", "givenName" => "Kristian J.", "familyName" => "Garza", "affiliation" => ["Freie Universität Berlin"], "nameIdentifiers" => [{ "schemeUri" => "https://orcid.org", "nameIdentifier" => "https://orcid.org/0000-0003-3484-6875", "nameIdentifierScheme" => "ORCID" }] }]) + end + + it "returns dois with crossref funder id", vcr: true do + get "/v3/dois?funder-id=10.13039/501100009053", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("data", 0, "attributes", "fundingReferences")).to eq([{ "funderIdentifier" => "https://doi.org/10.13039/501100009053", "funderIdentifierType" => "Crossref Funder ID", "funderName" => "The Wellcome Trust DBT India Alliance" }]) + end + + it "returns dois with multiple crossref funder id", vcr: true do + get "/v3/dois?funder-id=10.13039/501100009053,10.13039/501100000735", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("data", 0, "attributes", "fundingReferences")).to eq([{ "funderIdentifier" => "https://doi.org/10.13039/501100009053", "funderIdentifierType" => "Crossref Funder ID", "funderName" => "The Wellcome Trust DBT India Alliance" }]) + end + + it "returns dois with crossref funder id as url", vcr: true do + get "/v3/dois?funder-id=https://doi.org/10.13039/501100009053", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("data", 0, "attributes", "fundingReferences")).to eq([{ "funderIdentifier" => "https://doi.org/10.13039/501100009053", "funderIdentifierType" => "Crossref Funder ID", "funderName" => "The Wellcome Trust DBT India Alliance" }]) + end + + it "returns dois with short ror id", vcr: true do + get "/v3/dois?affiliation-id=046ak2485&affiliation=true", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("data", 0, "attributes", "creators")).to eq([{ "name" => "Garza, Kristian J.", "nameType" => "Personal", "givenName" => "Kristian J.", "familyName" => "Garza", "affiliation" => [{ "name" => "Freie Universität Berlin", "affiliationIdentifier" => "https://ror.org/046ak2485", "affiliationIdentifierScheme" => "ROR" }], "nameIdentifiers" => [{ "schemeUri" => "https://orcid.org", "nameIdentifier" => "https://orcid.org/0000-0003-3484-6875", "nameIdentifierScheme" => "ORCID" }] }]) + end + + it "returns dois with ror id", vcr: true do + get "/v3/dois?affiliation-id=ror.org/046ak2485&affiliation=true", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("data", 0, "attributes", "creators")).to eq([{ "name" => "Garza, Kristian J.", "nameType" => "Personal", "givenName" => "Kristian J.", "familyName" => "Garza", "affiliation" => [{ "name" => "Freie Universität Berlin", "affiliationIdentifier" => "https://ror.org/046ak2485", "affiliationIdentifierScheme" => "ROR" }], "nameIdentifiers" => [{ "schemeUri" => "https://orcid.org", "nameIdentifier" => "https://orcid.org/0000-0003-3484-6875", "nameIdentifierScheme" => "ORCID" }] }]) + end + + it "returns dois with ror id as url", vcr: true do + get "/v3/dois?affiliation-id=https://ror.org/046ak2485&affiliation=true", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("data", 0, "attributes", "creators")).to eq([{ "name" => "Garza, Kristian J.", "nameType" => "Personal", "givenName" => "Kristian J.", "familyName" => "Garza", "affiliation" => [{ "name" => "Freie Universität Berlin", "affiliationIdentifier" => "https://ror.org/046ak2485", "affiliationIdentifierScheme" => "ROR" }], "nameIdentifiers" => [{ "schemeUri" => "https://orcid.org", "nameIdentifier" => "https://orcid.org/0000-0003-3484-6875", "nameIdentifierScheme" => "ORCID" }] }]) + end + + it "returns dois with re3data id", vcr: true do + get "/v3/dois?re3data-id=10.17616/R3XS37&include=client", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("included", 0, "attributes", "re3data")).to eq("https://doi.org/10.17616/r3xs37") + end + + it "returns dois with re3data id as url", vcr: true do + get "/v3/dois?re3data-id=https://doi.org/10.17616/R3XS37&include=client", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("included", 0, "attributes", "re3data")).to eq("https://doi.org/10.17616/r3xs37") + end + + it "returns dois with full name", vcr: true do + get "/v3/dois?query=Kristian%20Garza&affiliation=true", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("data", 0, "attributes", "creators")).to eq([{ "name" => "Garza, Kristian J.", "nameType" => "Personal", "givenName" => "Kristian J.", "familyName" => "Garza", "affiliation" => [{ "name" => "Freie Universität Berlin", "affiliationIdentifier" => "https://ror.org/046ak2485", "affiliationIdentifierScheme" => "ROR" }], "nameIdentifiers" => [{ "schemeUri" => "https://orcid.org", "nameIdentifier" => "https://orcid.org/0000-0003-3484-6875", "nameIdentifierScheme" => "ORCID" }] }]) + end + + it "returns dois with field of science", vcr: true do + get "/v3/dois?field-of-science=computer_and_information_sciences", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("meta", "fieldsOfScience")).to eq([{ "count" => 1, "id" => "computer_and_information_sciences", "title" => "Computer and information sciences" }]) + expect(json.dig("data", 0, "attributes", "creators")).to eq([{ "name" => "Garza, Kristian J.", "nameType" => "Personal", "givenName" => "Kristian J.", "familyName" => "Garza", "affiliation" => ["Freie Universität Berlin"], "nameIdentifiers" => [{ "schemeUri" => "https://orcid.org", "nameIdentifier" => "https://orcid.org/0000-0003-3484-6875", "nameIdentifierScheme" => "ORCID" }] }]) + end + end + + describe "GET /v3/dois/:id", elasticsearch: true do + let!(:doi) { create(:doi, client: client) } + + before do + DataciteDoi.import + sleep 2 + end + + context "when the record exists" do + it "returns the Doi" do + get "/v3/dois/#{doi.doi}", nil, headers + + expect(last_response.status).to eq(200) + result = json.dig("data") + + expect(result.dig("attributes", "doi")).to eq(doi.doi.downcase) + expect(result.dig("attributes", "titles")).to eq(doi.titles) + expect(result.dig("attributes", "identifiers")).to eq([{ "identifier" => "pk-1234", "identifierType" => "publisher ID" }]) + expect(result.dig("attributes", "alternateIdentifiers")).to eq([{ "alternateIdentifier" => "pk-1234", "alternateIdentifierType" => "publisher ID" }]) + end + end + + context "when the record does not exist" do + it "returns status code 404" do + get "/v3/dois/10.5256/xxxx", nil, headers + + expect(last_response.status).to eq(404) + expect(json).to eq("errors" => [{ "status" => "404", "title" => "The resource you are looking for doesn't exist." }]) + end + end + + context "provider_admin" do + let(:provider_bearer) { Client.generate_token(role_id: "provider_admin", uid: provider.symbol, provider_id: provider.symbol.downcase, password: provider.password) } + let(:provider_headers) { { "HTTP_ACCEPT" => "application/vnd.api+json", "HTTP_AUTHORIZATION" => "Bearer " + provider_bearer } } + + it "returns the Doi" do + get "/v3/dois/#{doi.doi}", nil, provider_headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi.downcase) + end + end + + context "anonymous user" do + it "returns the Doi" do + get "/v3/dois/#{doi.doi}" + + expect(last_response.status).to eq(404) + expect(json).to eq("errors" => [{ "status" => "404", "title" => "The resource you are looking for doesn't exist." }]) + end + end + + context "creators started as an object not array" do + let(:doi) do + create(:doi, client: client, creators: + { + "nameType" => "Personal", + "name" => "John Doe", + "affiliation" => [], + "nameIdentifiers" => [], + }) + end + + it "returns the creators as list" do + get "/v3/dois/#{doi.doi}", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "creators")).to eq([doi.creators]) + end + end + + context "nameIdentifiers started as an object not array" do + let(:doi) do + create(:doi, client: client, creators: + [{ + "nameType" => "Personal", + "name" => "John Doe", + "affiliation" => [], + "nameIdentifiers": { + "nameIdentifier": "http://viaf.org/viaf/4934600", + "nameIdentifierScheme": "VIAF" + }, + }]) + end + + it "returns the nameIdentifiers as list" do + get "/v3/dois/#{doi.doi}", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "creators", 0, "nameIdentifiers")).to eq([{ "nameIdentifier" => "http://viaf.org/viaf/4934600", "nameIdentifierScheme" => "VIAF" }]) + end + end + end + + describe "GET /v3/dois for dissertations", elasticsearch: true, vcr: true do + let!(:dois) { create_list(:doi, 3, types: { "resourceTypeGeneral" => "Text", "resourceType" => "Dissertation" }, client: client, aasm_state: "findable") } + + before do + DataciteDoi.import + sleep 3 + end + + it "filter for dissertations" do + get "/v3/dois?resource-type=Dissertation", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(3) + expect(json.dig("meta", "total")).to eq(3) + expect(json.dig("data", 0, "attributes", "publicationYear")).to eq(2011) + expect(json.dig("data", 0, "attributes", "types")).to eq("bibtex" => "phdthesis", "citeproc" => "thesis", "resourceType" => "Dissertation", "resourceTypeGeneral" => "Text", "ris" => "THES", "schemaOrg" => "Thesis") + end + end + + describe "GET /v3/dois for instruments", elasticsearch: true, vcr: true do + let!(:dois) { create_list(:doi, 3, types: { "resourceTypeGeneral" => "Other", "resourceType" => "Instrument" }, client: client, aasm_state: "findable") } + + before do + DataciteDoi.import + sleep 3 + end + + it "filter for instruments" do + get "/v3/dois?resource-type=Instrument", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(3) + expect(json.dig("meta", "total")).to eq(3) + expect(json.dig("data", 0, "attributes", "publicationYear")).to eq(2011) + expect(json.dig("data", 0, "attributes", "types")).to eq("bibtex" => "misc", "citeproc" => "article", "resourceType" => "Instrument", "resourceTypeGeneral" => "Other", "ris" => "GEN", "schemaOrg" => "CreativeWork") + end + end + + describe "GET /v3/dois for interactive resources", elasticsearch: true, vcr: true do + let!(:dois) { create_list(:doi, 3, types: { "resourceTypeGeneral" => "InteractiveResource", "resourceType" => "Presentation" }, client: client, aasm_state: "findable") } + + before do + DataciteDoi.import + sleep 3 + end + + it "filter for interactive resources" do + get "/v3/dois?resource-type-id=interactive-resource", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(3) + expect(json.dig("meta", "total")).to eq(3) + expect(json.dig("data", 0, "attributes", "publicationYear")).to eq(2011) + expect(json.dig("data", 0, "attributes", "types")).to eq("bibtex" => "misc", "citeproc" => "article", "resourceType" => "Presentation", "resourceTypeGeneral" => "InteractiveResource", "ris" => "GEN", "schemaOrg" => "CreativeWork") + expect(json.dig("meta", "resourceTypes")).to eq([{ "count" => 3, "id" => "interactive-resource", "title" => "Interactive Resource" }]) + end + + it "filter for interactive resources no facets" do + get "/v3/dois?resource-type-id=interactive-resource&disable-facets=true", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(3) + expect(json.dig("meta", "total")).to eq(3) + expect(json.dig("data", 0, "attributes", "publicationYear")).to eq(2011) + expect(json.dig("data", 0, "attributes", "types")).to eq("bibtex" => "misc", "citeproc" => "article", "resourceType" => "Presentation", "resourceTypeGeneral" => "InteractiveResource", "ris" => "GEN", "schemaOrg" => "CreativeWork") + expect(json.dig("meta")).to eq("page" => 1, "total" => 3, "totalPages" => 1) + end + end + + describe "GET /v3/dois for fake resources", elasticsearch: true, vcr: true do + let!(:dois) { create_list(:doi, 3, types: { "resourceTypeGeneral" => "Fake", "resourceType" => "Presentation" }, client: client) } + + before do + DataciteDoi.import + sleep 3 + end + + it "filter for fake resources" do + get "/v3/dois?resource-type-id=fake", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(3) + expect(json.dig("meta", "total")).to eq(3) + expect(json.dig("data", 0, "attributes", "publicationYear")).to eq(2011) + expect(json.dig("data", 0, "attributes", "types")).to eq("bibtex" => "misc", "citeproc" => "article", "resourceType" => "Presentation", "resourceTypeGeneral" => "Fake", "ris" => "GEN", "schemaOrg" => "CreativeWork") + expect(json.dig("meta", "resourceTypes")).to eq([]) + end + end + + describe "GET /v3/dois with views and downloads", elasticsearch: true, vcr: true do + let(:doi) { create(:doi, client: client, aasm_state: "findable") } + let!(:views) { create_list(:event_for_datacite_investigations, 2, obj_id: doi.doi) } + let!(:downloads) { create_list(:event_for_datacite_requests, 2, obj_id: doi.doi) } + + before do + Event.import + DataciteDoi.import + sleep 3 + end + + # TODO aggregations in meta should not be by publication year + it "includes events" do + get "/v3/dois", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(1) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("meta", "views")).to eq([{ "count" => 50, "id" => "2011", "title" => "2011" }]) + expect(json.dig("meta", "downloads")).to eq([{ "count" => 20, "id" => "2011", "title" => "2011" }]) + expect(json.dig("data", 0, "attributes", "publicationYear")).to eq(2011) + expect(json.dig("data", 0, "attributes", "doi")).to eq(doi.doi.downcase) + expect(json.dig("data", 0, "attributes", "titles")).to eq(doi.titles) + expect(json.dig("data", 0, "attributes", "viewCount")).to eq(50) + expect(json.dig("data", 0, "attributes", "downloadCount")).to eq(20) + end + end + + describe "views", elasticsearch: true, vcr: true do + let(:doi) { create(:doi, client: client, aasm_state: "findable") } + let!(:views) { create_list(:event_for_datacite_investigations, 3, obj_id: "https://doi.org/#{doi.doi}", relation_type_id: "unique-dataset-investigations-regular", total: 25) } + + before do + DataciteDoi.import + Event.import + sleep 2 + end + + it "has views" do + get "/v3/dois/#{doi.doi}", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "url")).to eq(doi.url) + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi.downcase) + expect(json.dig("data", "attributes", "titles")).to eq(doi.titles) + expect(json.dig("data", "attributes", "viewCount")).to eq(75) + expect(json.dig("data", "attributes", "viewsOverTime")).to eq([{ "total" => 25, "yearMonth" => "2015-06" }, { "total" => 25, "yearMonth" => "2015-06" }, { "total" => 25, "yearMonth" => "2015-06" }]) + end + + it "has views meta" do + get "/v3/dois", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("meta", "views")).to eq([{ "count" => 75, "id" => "2011", "title" => "2011" }]) + end + end + + describe "downloads", elasticsearch: true, vcr: true do + let(:doi) { create(:doi, client: client, aasm_state: "findable") } + let!(:downloads) { create_list(:event_for_datacite_investigations, 3, obj_id: "https://doi.org/#{doi.doi}", relation_type_id: "unique-dataset-requests-regular", total: 10) } + + before do + DataciteDoi.import + Event.import + sleep 2 + end + + it "has downloads" do + get "/v3/dois/#{doi.doi}", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "url")).to eq(doi.url) + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi.downcase) + expect(json.dig("data", "attributes", "titles")).to eq(doi.titles) + expect(json.dig("data", "attributes", "downloadCount")).to eq(30) + expect(json.dig("data", "attributes", "downloadsOverTime")).to eq([{ "total" => 10, "yearMonth" => "2015-06" }, { "total" => 10, "yearMonth" => "2015-06" }, { "total" => 10, "yearMonth" => "2015-06" }]) + end + + it "has downloads meta" do + get "/v3/dois", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("meta", "downloads")).to eq([{ "count" => 30, "id" => "2011", "title" => "2011" }]) + end + end + + # describe "references", elasticsearch: true, vcr: true do + # let(:doi) { create(:doi, client: client, aasm_state: "findable") } + # let(:target_doi) { create(:doi, client: client, aasm_state: "findable") } + # let!(:reference_event) { create(:event_for_crossref, subj_id: "https://doi.org/#{doi.doi}", obj_id: "https://doi.org/#{target_doi.doi}", relation_type_id: "references") } + + # before do + # DataciteDoi.import + # Event.import + # sleep 2 + # end + + # it "has references" do + # get "/dois/#{doi.doi}?include=references", nil, headers + + # expect(last_response.status).to eq(200) + # expect(json.dig('data', 'attributes', 'url')).to eq(doi.url) + # expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) + # expect(json.dig('data', 'attributes', 'titles')).to eq(doi.titles) + # expect(json.dig('data', 'attributes', 'referenceCount')).to eq(1) + # expect(json.dig('data', 'relationships', 'references', 'data')).to eq([{"id"=>target_doi.doi.downcase, "type"=>"dois"}]) + # # TODO fix included + # # expect(json.dig('included').length).to eq(1) + # # expect(json.dig('included', 0, 'attributes', 'doi')).to eq(target_doi.doi.downcase) + # end + # end + + # describe "citations", elasticsearch: true, vcr: true do + # let(:doi) { create(:doi, client: client, aasm_state: "findable") } + # let(:source_doi) { create(:doi, client: client, aasm_state: "findable") } + # let!(:citation_event) { create(:event_for_datacite_crossref, subj_id: "https://doi.org/#{doi.doi}", obj_id: "https://doi.org/#{source_doi.doi}", relation_type_id: "is-referenced-by") } + + # before do + # DataciteDoi.import + # Event.import + # sleep 2 + # end + + # it "has citations" do + # get "/dois/#{doi.doi}?include=citations", nil, headers + + # expect(last_response.status).to eq(200) + # expect(json.dig('data', 'attributes', 'url')).to eq(doi.url) + # expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) + # expect(json.dig('data', 'attributes', 'titles')).to eq(doi.titles) + # expect(json.dig('data', 'attributes', 'citationCount')).to eq(1) + # expect(json.dig('data', 'attributes', 'citationsOverTime')).to eq([{"total"=>1, "year"=>"2020"}]) + # expect(json.dig('data', 'relationships', 'citations', 'data')).to eq([{"id"=>source_doi.doi.downcase, "type"=>"dois"}]) + # # TODO fix included + # # expect(json.dig('included').length).to eq(1) + # # expect(json.dig('included', 0, 'attributes', 'doi')).to eq(source_doi.doi.downcase) + # end + + # it "has citations meta" do + # get "/dois", nil, headers + + # expect(last_response.status).to eq(200) + # expect(json.dig('meta', 'citations')).to eq([{"count"=>1, "id"=>"2011", "title"=>"2011"}]) + # end + # end + + # describe "parts", elasticsearch: true, vcr: true do + # let(:doi) { create(:doi, client: client, aasm_state: "findable") } + # let(:target_doi) { create(:doi, client: client, aasm_state: "findable") } + # let!(:part_events) { create(:event_for_datacite_parts, subj_id: "https://doi.org/#{doi.doi}", obj_id: "https://doi.org/#{target_doi.doi}", relation_type_id: "has-part") } + + # before do + # DataciteDoi.import + # Event.import + # sleep 2 + # end + + # it "has parts" do + # get "/dois/#{doi.doi}?include=parts", nil, headers + + # expect(last_response.status).to eq(200) + # expect(json.dig('data', 'attributes', 'url')).to eq(doi.url) + # expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) + # expect(json.dig('data', 'attributes', 'titles')).to eq(doi.titles) + # expect(json.dig('data', 'attributes', 'partCount')).to eq(1) + # expect(json.dig('data', 'relationships', 'parts', 'data')).to eq([{"id"=>target_doi.doi.downcase, "type"=>"dois"}]) + # # TODO fix included + # # expect(json.dig('included').length).to eq(1) + # # expect(json.dig('included', 0, 'attributes', 'doi')).to eq(target_doi.doi.downcase) + # end + # end + + # describe "versions", elasticsearch: true, vcr: true do + # let(:doi) { create(:doi, client: client, aasm_state: "findable") } + # let(:target_doi) { create(:doi, client: client, aasm_state: "findable") } + # let!(:version_events) { create(:event_for_datacite_parts, subj_id: "https://doi.org/#{doi.doi}", obj_id: "https://doi.org/#{target_doi.doi}", relation_type_id: "has-version") } + + # before do + # DataciteDoi.import + # Event.import + # sleep 2 + # end + + # it "has versions" do + # get "/dois/#{doi.doi}?include=versions", nil, headers + + # expect(last_response.status).to eq(200) + # expect(json.dig('data', 'attributes', 'url')).to eq(doi.url) + # expect(json.dig('data', 'attributes', 'doi')).to eq(doi.doi.downcase) + # expect(json.dig('data', 'attributes', 'titles')).to eq(doi.titles) + # expect(json.dig('data', 'attributes', 'versionCount')).to eq(1) + # expect(json.dig('data', 'relationships', 'versions', 'data')).to eq([{"id"=>target_doi.doi.downcase, "type"=>"dois"}]) + # # TODO fix included + # # expect(json.dig('included').length).to eq(1) + # # expect(json.dig('included', 0, 'attributes', 'doi')).to eq(target_doi.doi.downcase) + # end + # end + + describe "state" do + let(:doi_id) { "10.14454/4K3M-NYVG" } + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:bearer) { User.generate_token(role_id: "client_admin", client_id: client.symbol.downcase) } + let(:headers) { { "HTTP_ACCEPT" => "application/vnd.api+json", "HTTP_AUTHORIZATION" => "Bearer " + bearer } } + + context "initial state draft", elasticsearch: true do + let!(:doi) { create(:doi, client: client) } + + before do + DataciteDoi.import + sleep 2 + end + + it "fetches the record" do + get "/v3/dois/#{doi.doi}", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "url")).to eq(doi.url) + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi.downcase) + expect(json.dig("data", "attributes", "titles")).to eq(doi.titles) + expect(json.dig("data", "attributes", "isActive")).to be false + expect(json.dig("data", "attributes", "state")).to eq("draft") + end + end + + context "register" do + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "xml" => xml, + "url" => "http://www.bl.uk/pdf/pat.pdf", + "event" => "register", + }, + }, + } + end + + it "creates the record" do + patch "/v3/dois/#{doi_id}", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "doi")).to eq(doi_id.downcase) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/pat.pdf") + expect(json.dig("data", "attributes", "isActive")).to be false + expect(json.dig("data", "attributes", "state")).to eq("registered") + end + end + + context "register no url" do + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "xml" => xml, + "event" => "register", + }, + }, + } + end + + it "creates the record" do + patch "/v3/dois/#{doi_id}", valid_attributes, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq([{ "source" => "url", "title" => "Can't be blank", "uid" => "10.14454/4k3m-nyvg" }]) + end + end + + context "publish" do + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "xml" => xml, + "event" => "publish", + }, + }, + } + end + + it "updates the record" do + patch "/v3/dois/#{doi_id}", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "doi")).to eq(doi_id.downcase) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/pat.pdf") + expect(json.dig("data", "attributes", "isActive")).to be true + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + end + + context "publish no url" do + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "xml" => xml, + "event" => "publish", + }, + }, + } + end + + it "updates the record" do + patch "/v3/dois/#{doi_id}", valid_attributes, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq([{ "source" => "url", "title" => "Can't be blank", "uid" => "10.14454/4k3m-nyvg" }]) + end + end + + context "hide" do + let(:doi) { create(:doi, doi: "10.14454/1x4x-9056", client: client, url: "https://datacite.org", aasm_state: "findable") } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "event" => "hide", + }, + }, + } + end + + it "updates the record" do + patch "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi.downcase) + expect(json.dig("data", "attributes", "isActive")).to be false + expect(json.dig("data", "attributes", "state")).to eq("registered") + end + end + + context "hide with reason" do + let(:doi) { create(:doi, doi: "10.14454/0etfa87k9p", client: client, url: "https://datacite.org", aasm_state: "findable") } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "event" => "hide", + "reason" => "withdrawn by author", + }, + }, + } + end + + it "updates the record" do + patch "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi.downcase) + expect(json.dig("data", "attributes", "isActive")).to be false + expect(json.dig("data", "attributes", "reason")).to eq("withdrawn by author") + expect(json.dig("data", "attributes", "state")).to eq("registered") + end + end + end + + describe "PATCH /v3/dois/:id" do + context "when the record exists" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "xml" => xml, + }, + }, + } + end + + it "updates the record" do + patch "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/pat.pdf") + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi.downcase) + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Eating your own Dog Food" }]) + end + + it "sets state to draft" do + patch "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(json.dig("data", "attributes", "state")).to eq("draft") + end + end + + context "read-only attributes" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "xml" => xml, + "container" => {}, + "published" => nil, + "viewsOverTime" => {}, + "downloadsOverTime" => {}, + "citationsOverTime" => {}, + "viewCount" => 0, + "downloadCount" => 0, + "citationCount" => 0, + "partCount" => 0, + "partOfCount" => 0, + "referenceCount" => 0, + "versionCount" => 0, + "versionOfCount" => 0, + }, + }, + } + end + + it "updates the record" do + patch "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/pat.pdf") + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi.downcase) + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Eating your own Dog Food" }]) + end + end + + context "when the record exists no data attribute" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:valid_attributes) do + { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "xml" => xml, + } + end + + it "raises an error" do + patch "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(400) + expect(json.dig("errors")).to eq([{ "status" => "400", "title" => "You need to provide a payload following the JSONAPI spec" }]) + end + end + + context "update sizes" do + let(:doi) { create(:doi, doi: "10.14454/10703", url: "https://datacite.org", client: client) } + let(:sizes) { ["100 samples", "56 pages"] } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "sizes" => sizes, + "event" => "publish", + }, + }, + } + end + + it "updates the doi" do + put "/v3/dois/#{doi.doi}", valid_attributes, admin_headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "sizes")).to eq(sizes) + end + end + + context "update formats" do + let(:doi) { create(:doi, doi: "10.14454/10703", url: "https://datacite.org", client: client) } + let(:formats) { ["application/json"] } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "formats" => formats, + "event" => "publish", + }, + }, + } + end + + it "updates the doi" do + put "/v3/dois/#{doi.doi}", valid_attributes, admin_headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "formats")).to eq(formats) + end + end + + context "no creators validate" do + let(:doi) { create(:doi, client: client, creators: nil) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "xml" => Base64.strict_encode64(doi.xml), + "event" => "publish", + }, + }, + } + end + + it "returns error" do + put "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq([{ "source" => "creators", "title" => "DOI #{doi.uid}: Missing child element(s). Expected is ( {http://datacite.org/schema/kernel-4}creator ). at line 4, column 0", "uid" => doi.uid }]) + end + end + + context "when the record exists https://github.com/datacite/lupo/issues/89" do + let(:doi) { create(:doi, doi: "10.14454/119496", url: "https://datacite.org", client: client) } + let(:valid_attributes) { JSON.parse(file_fixture("datacite_89.json").read) } + + it "returns no errors" do + put "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi) + end + end + + context "schema 2.2" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite_schema_2.2.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "xml" => xml, + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "event" => "publish", + }, + }, + } + end + + it "returns status code 422" do + patch "/v3/dois/10.14454/10703", valid_attributes, headers + + expect(last_response.status).to eq(422) + expect(json.fetch("errors", nil)).to eq([{ "source" => "xml", "title" => "DOI 10.14454/10703: Schema http://datacite.org/schema/kernel-2.2 is no longer supported", "uid" => "10.14454/10703" }]) + end + end + + context "NoMethodError https://github.com/datacite/lupo/issues/84" do + let(:doi) { create(:doi, doi: "10.14454/4K3M-NYVG", client: client) } + let(:url) { "https://figshare.com/articles/Additional_file_1_of_Contemporary_ancestor_Adaptive_divergence_from_standing_genetic_variation_in_Pacific_marine_threespine_stickleback/6839054/1" } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => url, + "xml" => Base64.strict_encode64(doi.xml), + "event" => "publish", + }, + }, + } + end + + it "returns no errors" do + put "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi.downcase) + expect(json.dig("data", "attributes", "url")).to eq(url) + end + end + + context "when the record doesn't exist" do + let(:doi_id) { "10.14454/4K3M-NYVG" } + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "xml" => xml, + "event" => "publish", + }, + }, + } + end + + it "creates the record" do + put "/v3/dois/#{doi_id}", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/pat.pdf") + expect(json.dig("data", "attributes", "doi")).to eq(doi_id.downcase) + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Eating your own Dog Food" }]) + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + end + + context "when the record doesn't exist no creators publish" do + let(:doi_id) { "10.14454/077d-fj48" } + let(:xml) { Base64.strict_encode64(file_fixture("datacite_missing_creator.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "xml" => xml, + "event" => "publish", + }, + }, + } + end + + it "returns error" do + put "/v3/dois/#{doi_id}", valid_attributes, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq([{ "source" => "creators", "title" => "DOI #{doi_id}: Missing child element(s). Expected is ( {http://datacite.org/schema/kernel-4}creator ). at line 4, column 0", "uid" => "10.14454/077d-fj48" }]) + end + end + + # no difference whether creators is nil, or attribute missing (see previous test) + context "when the record doesn't exist no creators publish with json" do + let(:doi_id) { "10.14454/077d-fj48" } + let(:xml) { Base64.strict_encode64(file_fixture("datacite_missing_creator.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "creators" => nil, + "xml" => xml, + "event" => "publish", + }, + }, + } + end + + it "returns error" do + put "/v3/dois/#{doi_id}", valid_attributes, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq([{ "source" => "creators", "title" => "DOI 10.14454/077d-fj48: Missing child element(s). Expected is ( {http://datacite.org/schema/kernel-4}creator ). at line 4, column 0", "uid" => "10.14454/077d-fj48" }]) + end + end + + context "when the record exists with conversion" do + let(:xml) { Base64.strict_encode64(file_fixture("crossref.bib").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "xml" => xml, + }, + }, + } + end + + it "updates the record" do + patch "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/pat.pdf") + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi.downcase) + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth" }]) + end + + it "sets state to registered" do + patch "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(json.dig("data", "attributes", "state")).to eq("draft") + end + end + + context "when the date issued is changed to :tba" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "xml" => xml, + "dates" => { + "date" => ":tba", + "dateType" => "Issued", + }, + "event" => "publish", + }, + }, + } + end + + it "updates the record" do + patch "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/pat.pdf") + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi.downcase) + expect(json.dig("data", "attributes", "dates")).to eq([{ "date" => ":tba", "dateType" => "Issued" }]) + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + end + + context "when the title is changed" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:titles) { [{ "title" => "Submitted chemical data for InChIKey=YAPQBXQYLJRXSA-UHFFFAOYSA-N" }] } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "xml" => xml, + "titles" => titles, + "event" => "publish", + }, + }, + } + end + + it "updates the record" do + patch "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/pat.pdf") + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi.downcase) + expect(json.dig("data", "attributes", "titles")).to eq(titles) + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + end + + context "when the title is changed wrong format" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:titles) { "Submitted chemical data for InChIKey=YAPQBXQYLJRXSA-UHFFFAOYSA-N" } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "xml" => xml, + "titles" => titles, + "event" => "publish", + }, + }, + } + end + + it "error" do + patch "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq([{ "source" => "titles", "title" => "Title 'Submitted chemical data for InChIKey=YAPQBXQYLJRXSA-UHFFFAOYSA-N' should be an object instead of a string.", "uid" => "10.14454/4k3m-nyvg" }]) + end + end + + context "when the description is changed to empty" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:descriptions) { [] } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "xml" => xml, + "descriptions" => descriptions, + "event" => "publish", + }, + }, + } + end + + it "updates the record" do + patch "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/pat.pdf") + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi.downcase) + expect(json.dig("data", "attributes", "descriptions")).to eq([]) + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + end + + context "when the xml field has datacite_json" do + let(:doi_id) { "10.14454/077d-fj48" } + let(:xml) { Base64.strict_encode64(file_fixture("datacite-user-example.json").read) } + let(:valid_attributes) do + { + "data" => { + "id" => doi_id, + "attributes" => { + "doi" => doi_id, + "xml" => xml, + "event" => "publish", + }, + "type" => "dois", + }, + } + end + + it "updates the record" do + patch "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi.downcase) + expect(json.dig("data", "attributes", "titles", 0, "title")).to eq("The Relationship Among Sport Type, Micronutrient Intake and Bone Mineral Density in an Athlete Population") + expect(json.dig("data", "attributes", "descriptions", 0, "description")).to start_with("Diet and physical activity are two modifiable factors that can curtail the development of osteoporosis in the aging population. ") + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + end + + context "when a doi is created ignore reverting back" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "source" => "test", + "event" => "publish", + }, + }, + } + end + let(:undo_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => doi.doi, + }, + }, + } + end + + it "creates the record" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Eating your own Dog Food" }]) + end + + it "revert the changes" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(201) + post "/v3/dois/undo", undo_attributes, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Data from: A new malaria agent in African hominids." }]) + end + end + + context "when the title is changed and reverted back" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:titles) { [{ "title" => "Submitted chemical data for InChIKey=YAPQBXQYLJRXSA-UHFFFAOYSA-N" }] } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "xml" => xml, + "titles" => titles, + "event" => "publish", + }, + }, + } + end + let(:undo_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => doi.doi, + }, + }, + } + end + + it "updates the record" do + patch "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "titles")).to eq(titles) + end + + it "revert the changes" do + patch "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(200) + post "/v3/dois/undo", undo_attributes, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Data from: A new malaria agent in African hominids." }]) + end + end + + context "when the creators change" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:creators) { [{ "affiliation" => [], "nameIdentifiers" => [], "name" => "Ollomi, Benjamin" }, { "affiliation" => [], "nameIdentifiers" => [], "name" => "Duran, Patrick" }] } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "xml" => xml, + "creators" => creators, + "event" => "publish", + }, + }, + } + end + + it "updates the record" do + patch "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/pat.pdf") + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi.downcase) + expect(json.dig("data", "attributes", "creators")).to eq(creators) + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + end + + context "fail when we transfer a DOI as provider" do + let(:provider_bearer) { User.generate_token(uid: "datacite", role_id: "provider_admin", name: "DataCite", email: "support@datacite.org", provider_id: "datacite") } + let(:provider_headers) { { "HTTP_ACCEPT" => "application/vnd.api+json", "CONTENT_TYPE" => "application/vnd.api+json", "HTTP_AUTHORIZATION" => "Bearer " + provider_bearer } } + + let(:doi) { create(:doi, client: client) } + let(:new_client) { create(:client, symbol: "#{provider.symbol}.magic", provider: provider, password: ENV["MDS_PASSWORD"]) } + + #  attributes MUST be empty + let(:valid_attributes) { file_fixture("transfer.json").read } + + it "returns errors" do + put "/v3/dois/#{doi.doi}", valid_attributes.to_json, provider_headers + + expect(last_response.status).to eq(403) + end + end + + context "passes when we transfer a DOI as provider" do + let(:provider_bearer) { User.generate_token(uid: "datacite", role_id: "provider_admin", name: "DataCite", email: "support@datacite.org", provider_id: "datacite") } + let(:provider_headers) { { "HTTP_ACCEPT" => "application/vnd.api+json", "CONTENT_TYPE" => "application/vnd.api+json", "HTTP_AUTHORIZATION" => "Bearer " + provider_bearer } } + + let(:doi) { create(:doi, client: client) } + let(:new_client) { create(:client, symbol: "#{provider.symbol}.M", provider: provider, password: ENV["MDS_PASSWORD"]) } + + #  attributes MUST be empty + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "mode" => "transfer", + }, + "relationships" => { + "client" => { + "data" => { + "type" => "clients", + "id" => new_client.symbol.downcase, + }, + }, + }, + }, + } + end + + it "updates the client id" do + put "/v3/dois/#{doi.doi}", valid_attributes.to_json, provider_headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi.downcase) + expect(json.dig("data", "relationships", "client", "data", "id")).to eq(new_client.symbol.downcase) + expect(json.dig("data", "attributes", "titles")).to eq(doi.titles) + end + end + + context "when we transfer a DOI as staff" do + let(:doi) { create(:doi, doi: "10.14454/119495", url: "http://www.bl.uk/pdf/pat.pdf", client: client, aasm_state: "registered") } + let(:new_client) { create(:client, symbol: "#{provider.symbol}.M", provider: provider, password: ENV["MDS_PASSWORD"]) } + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "mode" => "transfer", + }, + "relationships" => { + "client" => { + "data" => { + "type" => "clients", + "id" => new_client.symbol.downcase, + }, + }, + }, + }, + } + end + + it "updates the client id" do + put "/v3/dois/#{doi.doi}", valid_attributes, admin_headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi) + expect(json.dig("data", "relationships", "client", "data", "id")).to eq(new_client.symbol.downcase) + end + end + + context "when the resource_type_general changes" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:types) { { "resourceTypeGeneral" => "DataPaper", "resourceType" => "BlogPosting" } } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "http://www.bl.uk/pdf/pat.pdf", + "xml" => xml, + "types" => types, + "event" => "publish", + }, + }, + } + end + + it "updates the record" do + patch "/v3/dois/#{doi.doi}", valid_attributes, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/pat.pdf") + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi.downcase) + expect(json.dig("data", "attributes", "types")).to eq("bibtex" => "misc", "citeproc" => "article", "resourceType" => "BlogPosting", "resourceTypeGeneral" => "DataPaper", "ris" => "GEN", "schemaOrg" => "CreativeWork") + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + end + end + + describe "POST /v3/dois" do + context "when the request is valid" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "source" => "test", + "event" => "publish", + }, + }, + } + end + + it "creates a Doi" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/patspec.pdf") + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Eating your own Dog Food" }]) + expect(json.dig("data", "attributes", "creators")).to eq([{ "affiliation" => [], "familyName" => "Fenner", + "givenName" => "Martin", + "name" => "Fenner, Martin", + "nameIdentifiers" => + [{ "nameIdentifier" => "https://orcid.org/0000-0003-1419-2405", + "nameIdentifierScheme" => "ORCID", + "schemeUri" => "https://orcid.org" }] }]) + 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") + expect(json.dig("data", "attributes", "state")).to eq("findable") + + 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 + end + + context "when the request is valid no password" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "source" => "test", + "event" => "publish", + }, + }, + } + end + + it "fails to create a Doi" do + post "/v3/dois", valid_attributes + + expect(last_response.status).to eq(401) + end + end + + context "when the request has invalid client domains" do + let(:client) { create(:client, provider: provider, symbol: ENV["MDS_USERNAME"], password: ENV["MDS_PASSWORD"], re3data_id: "10.17616/r3xs37", domains: "example.org") } + 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(:headers) { { "HTTP_ACCEPT" => "application/vnd.api+json", "HTTP_AUTHORIZATION" => "Bearer " + bearer } } + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "source" => "test", + "event" => "publish", + }, + }, + } + end + + it "fails to create a Doi" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(422) + expect(json.dig("errors", 0, "title")).to end_with("is not allowed by repository #{doi.client.uid} domain settings.") + end + end + + context "when providing version" do + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + # "xml" => xml, + "source" => "test", + "version" => 45, + }, + }, + } + end + + it "create a draft Doi with version" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "version")).to eq("45") + end + end + + context "when the request is valid random doi" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "prefix" => "10.14454", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "source" => "test", + "event" => "publish", + }, + }, + } + end + + it "creates a Doi" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/patspec.pdf") + expect(json.dig("data", "attributes", "doi")).to start_with("10.14454") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Eating your own Dog Food" }]) + expect(json.dig("data", "attributes", "creators")).to eq([{ "affiliation" => [], "familyName" => "Fenner", + "givenName" => "Martin", + "name" => "Fenner, Martin", + "nameIdentifiers" => + [{ "nameIdentifier" => "https://orcid.org/0000-0003-1419-2405", + "nameIdentifierScheme" => "ORCID", + "schemeUri" => "https://orcid.org" }] }]) + 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") + expect(json.dig("data", "attributes", "state")).to eq("findable") + + doc = Nokogiri::XML(Base64.decode64(json.dig("data", "attributes", "xml")), nil, "UTF-8", &:noblanks) + expect(doc.at_css("identifier").content).to start_with("10.14454") + end + end + + context "when the request is valid with attributes" 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" }], + "source" => "test", + "event" => "publish", + }, + }, + } + end + + it "creates a Doi" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/patspec.pdf") + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Eating your own Dog Food" }]) + expect(json.dig("data", "attributes", "creators")).to eq([{ "affiliation" => [], "familyName" => "Fenner", "givenName" => "Martin", "nameIdentifiers" => [{ "nameIdentifier" => "https://orcid.org/0000-0003-1419-2405", "nameIdentifierScheme" => "ORCID", "schemeUri" => "https://orcid.org" }], "name" => "Fenner, Martin", "nameType" => "Personal" }]) + expect(json.dig("data", "attributes", "publisher")).to eq("DataCite") + expect(json.dig("data", "attributes", "publicationYear")).to eq(2016) + # 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") + expect(json.dig("data", "attributes", "state")).to eq("findable") + + 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 + end + + context "when the request is valid with recommended properties" 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" }], + "subjects" => [{ "subject" => "80505 Web Technologies (excl. Web Search)", + "schemeUri" => "http://www.abs.gov.au/ausstats/abs@.nsf/0/6BB427AB9696C225CA2574180004463E", + "subjectScheme" => "FOR", + "lang" => "en" }], + "contributors" => [{ "contributorType" => "DataManager", "familyName" => "Fenner", "givenName" => "Kurt", "nameIdentifiers" => [{ "nameIdentifier" => "https://orcid.org/0000-0003-1419-2401", "nameIdentifierScheme" => "ORCID", "schemeUri" => "https://orcid.org" }], "name" => "Fenner, Kurt", "nameType" => "Personal" }], + "dates" => [{ "date" => "2017-02-24", "dateType" => "Issued" }, { "date" => "2015-11-28", "dateType" => "Created" }, { "date" => "2017-02-24", "dateType" => "Updated" }], + "relatedIdentifiers" => [{ "relatedIdentifier" => "10.5438/55e5-t5c0", "relatedIdentifierType" => "DOI", "relationType" => "References" }], + "descriptions" => [ + { + "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", + }, + ], + "geoLocations" => [ + { + "geoLocationPoint" => { + "pointLatitude" => "49.0850736", + "pointLongitude" => "-123.3300992", + }, + }, + ], + "source" => "test", + "event" => "publish", + }, + }, + } + end + + it "creates a Doi" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/patspec.pdf") + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Eating your own Dog Food" }]) + expect(json.dig("data", "attributes", "creators")).to eq([{ "affiliation" => [], "familyName" => "Fenner", "givenName" => "Martin", "nameIdentifiers" => [{ "nameIdentifier" => "https://orcid.org/0000-0003-1419-2405", "nameIdentifierScheme" => "ORCID", "schemeUri" => "https://orcid.org" }], "name" => "Fenner, Martin", "nameType" => "Personal" }]) + expect(json.dig("data", "attributes", "publisher")).to eq("DataCite") + expect(json.dig("data", "attributes", "publicationYear")).to eq(2016) + expect(json.dig("data", "attributes", "subjects")).to eq([{ "lang" => "en", + "subject" => "80505 Web Technologies (excl. Web Search)", + "subjectScheme" => "FOR" }, + { "schemeUri" => "http://www.oecd.org/science/inno/38235147.pdf", + "subject" => "FOS: Computer and information sciences", + "subjectScheme" => "Fields of Science and Technology (FOS)" }, + { "subject" => "FOS: Computer and information sciences", + "subjectScheme" => "Fields of Science and Technology (FOS)" }]) + expect(json.dig("data", "attributes", "contributors")).to eq([{ "affiliation" => [], + "contributorType" => "DataManager", + "familyName" => "Fenner", + "givenName" => "Kurt", + "name" => "Fenner, Kurt", + "nameIdentifiers" => + [{ "nameIdentifier" => "https://orcid.org/0000-0003-1419-2401", + "nameIdentifierScheme" => "ORCID", + "schemeUri" => "https://orcid.org" }], + "nameType" => "Personal" }]) + expect(json.dig("data", "attributes", "dates")).to eq([{ "date" => "2017-02-24", "dateType" => "Issued" }, { "date" => "2015-11-28", "dateType" => "Created" }, { "date" => "2017-02-24", "dateType" => "Updated" }]) + expect(json.dig("data", "attributes", "relatedIdentifiers")).to eq([{ "relatedIdentifier" => "10.5438/55e5-t5c0", "relatedIdentifierType" => "DOI", "relationType" => "References" }]) + expect(json.dig("data", "attributes", "descriptions", 0, "description")).to start_with("Diet and physical activity") + expect(json.dig("data", "attributes", "geoLocations")).to eq([{ "geoLocationPoint" => { "pointLatitude" => "49.0850736", "pointLongitude" => "-123.3300992" } }]) + 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") + + 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") + expect(doc.at_css("subjects").content).to eq("80505 Web Technologies (excl. Web Search)") + expect(doc.at_css("contributors").content).to eq("Fenner, KurtKurtFennerhttps://orcid.org/0000-0003-1419-2401") + expect(doc.at_css("dates").content).to eq("2017-02-242015-11-282017-02-24") + expect(doc.at_css("relatedIdentifiers").content).to eq("10.5438/55e5-t5c0") + expect(doc.at_css("descriptions").content).to start_with("Diet and physical activity") + expect(doc.at_css("geoLocations").content).to eq("49.0850736-123.3300992") + end + end + + context "when the request is valid with optional properties" 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", + }, + }, + } + end + + it "creates a Doi" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/patspec.pdf") + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Eating your own Dog Food" }]) + expect(json.dig("data", "attributes", "creators")).to eq([{ "affiliation" => [], "familyName" => "Fenner", "givenName" => "Martin", "nameIdentifiers" => [{ "nameIdentifier" => "https://orcid.org/0000-0003-1419-2405", "nameIdentifierScheme" => "ORCID", "schemeUri" => "https://orcid.org" }], "name" => "Fenner, Martin", "nameType" => "Personal" }]) + expect(json.dig("data", "attributes", "publisher")).to eq("DataCite") + expect(json.dig("data", "attributes", "publicationYear")).to eq(2016) + # expect(json.dig('data', 'attributes', 'schemaVersion')).to eq("http://datacite.org/schema/kernel-4") + expect(json.dig("data", "attributes", "language")).to eq("en") + expect(json.dig("data", "attributes", "identifiers")).to eq([{ "identifier" => "123", "identifierType" => "Repository ID" }]) + expect(json.dig("data", "attributes", "alternateIdentifiers")).to eq([{ "alternateIdentifier" => "123", "alternateIdentifierType" => "Repository ID" }]) + expect(json.dig("data", "attributes", "rightsList")).to eq([{ "lang" => "en", "rights" => "Creative Commons Attribution 3.0", "rightsUri" => "http://creativecommons.org/licenses/by/3.0/" }]) + expect(json.dig("data", "attributes", "sizes")).to eq(["4 kB", "12.6 MB"]) + expect(json.dig("data", "attributes", "formats")).to eq(["application/pdf", "text/csv"]) + expect(json.dig("data", "attributes", "version")).to eq("1.1") + expect(json.dig("data", "attributes", "fundingReferences")).to eq([{ "funderIdentifier" => "https://doi.org/10.13039/501100009053", "funderIdentifierType" => "Crossref Funder ID", "funderName" => "The Wellcome Trust DBT India Alliance" }]) + 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") + + 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") + expect(doc.at_css("language").content).to eq("en") + expect(doc.at_css("alternateIdentifiers").content).to eq("123") + expect(doc.at_css("rightsList").content).to eq("Creative Commons Attribution 3.0") + expect(doc.at_css("sizes").content).to eq("4 kB12.6 MB") + expect(doc.at_css("formats").content).to eq("application/pdftext/csv") + expect(doc.at_css("version").content).to eq("1.1") + expect(doc.at_css("fundingReferences").content).to eq("The Wellcome Trust DBT India Alliancehttps://doi.org/10.13039/501100009053") + end + end + + context "with identifiers" do + let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("datacite-example-affiliation.xml"))) } + let(:params) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "xml" => xml, + "identifiers" => [{ "identifier" => "123", "identifierType" => "Repository ID" }], + }, + }, + } + end + + it "validates a Doi" do + post "/v3/dois", params, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "titles")).to eq([{ "lang" => "en-US", "title" => "Full DataCite XML Example" }, { "lang" => "en-US", "title" => "Demonstration of DataCite Properties.", "titleType" => "Subtitle" }]) + expect(json.dig("data", "attributes", "identifiers")).to eq([{ "identifier" => "123", "identifierType" => "Repository ID" }]) + expect(json.dig("data", "attributes", "alternateIdentifiers")).to eq([{ "alternateIdentifier" => "123", "alternateIdentifierType" => "Repository ID" }]) + + 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") + expect(doc.at_css("alternateIdentifiers").content).to eq("123") + end + end + + context "with identifiers that include DOI" do + let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("datacite-example-affiliation.xml"))) } + let(:params) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "xml" => xml, + "identifiers" => [{ "identifier" => "https://doi.org/10.14454/10703", "identifierType" => "DOI" }, { "identifier" => "123", "identifierType" => "Repository ID" }], + }, + }, + } + end + + it "validates a Doi" do + post "/v3/dois", params, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "titles")).to eq([{ "lang" => "en-US", "title" => "Full DataCite XML Example" }, { "lang" => "en-US", "title" => "Demonstration of DataCite Properties.", "titleType" => "Subtitle" }]) + expect(json.dig("data", "attributes", "identifiers")).to eq([{ "identifier" => "123", "identifierType" => "Repository ID" }]) + expect(json.dig("data", "attributes", "alternateIdentifiers")).to eq([{ "alternateIdentifier" => "123", "alternateIdentifierType" => "Repository ID" }]) + + 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") + expect(doc.at_css("alternateIdentifiers").content).to eq("123") + end + end + + context "with affiliation" do + let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("datacite-example-affiliation.xml"))) } + let(:params) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "xml" => xml, + }, + }, + } + end + + it "validates a Doi" do + post "/v3/dois", params, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "titles")).to eq([{ "lang" => "en-US", "title" => "Full DataCite XML Example" }, { "lang" => "en-US", "title" => "Demonstration of DataCite Properties.", "titleType" => "Subtitle" }]) + expect(json.dig("data", "attributes", "creators").length).to eq(3) + expect(json.dig("data", "attributes", "creators")[0]).to eq("affiliation" => ["DataCite"], + "familyName" => "Miller", + "givenName" => "Elizabeth", + "name" => "Miller, Elizabeth", + "nameIdentifiers" => [{ "nameIdentifier" => "https://orcid.org/0000-0001-5000-0007", "nameIdentifierScheme" => "ORCID", "schemeUri" => "https://orcid.org" }], + "nameType" => "Personal") + expect(json.dig("data", "attributes", "creators")[1]).to eq("affiliation" => ["Brown University", "Wesleyan University"], + "familyName" => "Carberry", + "givenName" => "Josiah", + "name" => "Carberry, Josiah", + "nameIdentifiers" => [{ "nameIdentifier" => "https://orcid.org/0000-0002-1825-0097", "nameIdentifierScheme" => "ORCID", "schemeUri" => "https://orcid.org" }], + "nameType" => "Personal") + expect(json.dig("data", "attributes", "creators")[2]).to eq("nameType" => "Organizational", "name" => "The Psychoceramics Study Group", "affiliation" => ["Brown University"], "nameIdentifiers" => []) + + xml = Maremma.from_xml(Base64.decode64(json.dig("data", "attributes", "xml"))).fetch("resource", {}) + expect(xml.dig("creators", "creator")[0]).to eq("affiliation" => { "__content__" => "DataCite", "affiliationIdentifier" => "https://ror.org/04wxnsj81", "affiliationIdentifierScheme" => "ROR" }, + "creatorName" => { "__content__" => "Miller, Elizabeth", "nameType" => "Personal" }, + "familyName" => "Miller", + "givenName" => "Elizabeth", + "nameIdentifier" => { "__content__" => "0000-0001-5000-0007", "nameIdentifierScheme" => "ORCID", "schemeURI" => "http://orcid.org/" }) + end + end + + context "with affiliation and query parameter" do + let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("datacite-example-affiliation.xml"))) } + let(:params) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "xml" => xml, + }, + }, + } + end + + it "validates a Doi" do + post "/v3/dois?affiliation=true", params, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "titles")).to eq([{ "lang" => "en-US", "title" => "Full DataCite XML Example" }, { "lang" => "en-US", "title" => "Demonstration of DataCite Properties.", "titleType" => "Subtitle" }]) + expect(json.dig("data", "attributes", "creators").length).to eq(3) + expect(json.dig("data", "attributes", "creators")[0]).to eq("affiliation" => [{ "affiliationIdentifierScheme" => "ROR", "affiliationIdentifier" => "https://ror.org/04wxnsj81", "name" => "DataCite" }], + "familyName" => "Miller", + "givenName" => "Elizabeth", + "name" => "Miller, Elizabeth", + "nameIdentifiers" => [{ "nameIdentifier" => "https://orcid.org/0000-0001-5000-0007", "nameIdentifierScheme" => "ORCID", "schemeUri" => "https://orcid.org" }], + "nameType" => "Personal") + expect(json.dig("data", "attributes", "creators")[1]).to eq("affiliation" => [{ "affiliationIdentifierScheme" => "ROR", "affiliationIdentifier" => "https://ror.org/05gq02987", "name" => "Brown University" }, { "affiliationIdentifierScheme" => "GRID", "affiliationIdentifier" => "https://grid.ac/institutes/grid.268117.b", "name" => "Wesleyan University" }], + "familyName" => "Carberry", + "givenName" => "Josiah", + "name" => "Carberry, Josiah", + "nameIdentifiers" => [{ "nameIdentifier" => "https://orcid.org/0000-0002-1825-0097", "nameIdentifierScheme" => "ORCID", "schemeUri" => "https://orcid.org" }], + "nameType" => "Personal") + expect(json.dig("data", "attributes", "creators")[2]).to eq("nameType" => "Organizational", "name" => "The Psychoceramics Study Group", "affiliation" => [{ "affiliationIdentifier" => "https://ror.org/05gq02987", "name" => "Brown University", "affiliationIdentifierScheme" => "ROR" }], "nameIdentifiers" => []) + + xml = Maremma.from_xml(Base64.decode64(json.dig("data", "attributes", "xml"))).fetch("resource", {}) + expect(xml.dig("creators", "creator")[0]).to eq("affiliation" => { "__content__" => "DataCite", "affiliationIdentifier" => "https://ror.org/04wxnsj81", "affiliationIdentifierScheme" => "ROR" }, + "creatorName" => { "__content__" => "Miller, Elizabeth", "nameType" => "Personal" }, + "familyName" => "Miller", + "givenName" => "Elizabeth", + "nameIdentifier" => { "__content__" => "0000-0001-5000-0007", "nameIdentifierScheme" => "ORCID", "schemeURI" => "http://orcid.org/" }) + end + end + + context "schema_org" do + let(:xml) { Base64.strict_encode64(file_fixture("schema_org_topmed.json").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "https://ors.datacite.org/doi:/10.14454/8na3-9s47", + "xml" => xml, + "source" => "test", + "event" => "publish", + }, + }, + } + end + + it "updates the record" do + patch "/v3/dois/10.14454/8na3-9s47", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "url")).to eq("https://ors.datacite.org/doi:/10.14454/8na3-9s47") + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/8na3-9s47") + expect(json.dig("data", "attributes", "contentUrl")).to eq(["s3://cgp-commons-public/topmed_open_access/197bc047-e917-55ed-852d-d563cdbc50e4/NWD165827.recab.cram", "gs://topmed-irc-share/public/NWD165827.recab.cram"]) + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "NWD165827.recab.cram" }]) + expect(json.dig("data", "attributes", "state")).to eq("findable") + + xml = Maremma.from_xml(Base64.decode64(json.dig("data", "attributes", "xml"))).fetch("resource", {}) + expect(xml.dig("titles", "title")).to eq("NWD165827.recab.cram") + end + end + + context "json" do + let(:attributes) do + JSON.parse(<<~HEREDOC, + { + "doi": "10.14454/9zwb-rb91", + "event": "publish", + "types": { + "resourceType": "Dissertation", + "resourceTypeGeneral": "Text" + }, + "creators": [ + { + "nameType": "Personal", + "givenName": "Julia M.", + "familyName": "Rovera", + "affiliation": [{ + "name": "Drexel University" + }], + "nameIdentifiers": [ + { + "schemeUri": "https://orcid.org", + "nameIdentifier": "https://orcid.org/0000-0001-7673-8253", + "nameIdentifierScheme": "ORCID" + } + ] + } + ], + "titles": [ + { + "lang": "en", + "title": "The Relationship Among Sport Type, Micronutrient Intake and Bone Mineral Density in an Athlete Population", + "titleType": null + }, + { + "lang": "en", + "title": "Subtitle", + "titleType": "Subtitle" + } + ], + "publisher": "Drexel University", + "publicationYear": 2019, + "descriptions": [ + { + "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" + } + ], + "url": "https://idea.library.drexel.edu/islandora/object/idea:9531" + } + HEREDOC + ) + end + + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => attributes, + "relationships" => { + "client" => { + "data" => { + "type" => "clients", + "id" => client.symbol.downcase, + }, + }, + }, + }, + } + end + + it "created the record" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "url")).to eq("https://idea.library.drexel.edu/islandora/object/idea:9531") + 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", "state")).to eq("findable") + + xml = Maremma.from_xml(Base64.decode64(json.dig("data", "attributes", "xml"))).fetch("resource", {}) + expect(xml.dig("titles", "title")).to eq([{ "__content__" => + "The Relationship Among Sport Type, Micronutrient Intake and Bone Mineral Density in an Athlete Population", + "xml:lang" => "en" }, + { "__content__" => "Subtitle", "titleType" => "Subtitle", "xml:lang" => "en" }]) + end + end + + context "datacite url", vcr: true do + let(:xml) { Base64.strict_encode64("https://doi.org/10.7272/q6g15xs4") } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "https://datashare.ucsf.edu/stash/dataset/doi:10.7272/Q6G15XS4", + "xml" => xml, + "source" => "test", + "event" => "publish", + }, + }, + } + end + + it "updates the record" do + patch "/v3/dois/10.14454/q6g15xs4", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "url")).to eq("https://datashare.ucsf.edu/stash/dataset/doi:10.7272/Q6G15XS4") + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/q6g15xs4") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "NEXUS Head CT" }]) + expect(json.dig("data", "attributes", "state")).to eq("findable") + + xml = Maremma.from_xml(Base64.decode64(json.dig("data", "attributes", "xml"))).fetch("resource", {}) + expect(xml.dig("titles", "title")).to eq("NEXUS Head CT") + end + end + + context "when the request uses schema 3" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite_schema_3.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "source" => "test", + "event" => "publish", + }, + }, + } + end + + it "creates a Doi" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/patspec.pdf") + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Data from: A new malaria agent in African hominids." }]) + expect(json.dig("data", "attributes", "source")).to eq("test") + expect(json.dig("data", "attributes", "schemaVersion")).to eq("http://datacite.org/schema/kernel-3") + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + end + + context "when the request creates schema 3 with funder contributor type" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite_schema_3.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "contributors" => [{ "contributorType" => "Funder", "name" => "Wellcome Trust", "nameType" => "Organizational" }], + "source" => "test", + "event" => "publish", + }, + }, + } + end + + it "creates a Doi" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(422) + expect(json.fetch("errors", nil)).to eq([{ "source" => "xml", "title" => "DOI 10.14454/10703: No matching global declaration available for the validation root. at line 2, column 0", "uid" => "10.14454/10703" }]) + end + end + + context "when the request has wrong object in nameIdentifiers" do + let(:valid_attributes) { JSON.parse(file_fixture("datacite_wrong_nameIdentifiers.json").read) } + + it "fails to create a Doi" do + post "/v3/dois", valid_attributes, headers + expect(last_response.status).to eq(201) + end + end + + context "when the request has wrong object in nameIdentifiers nasa" do + let(:valid_attributes) { JSON.parse(file_fixture("nasa_error.json").read) } + + it "fails to create a Doi" do + post "/v3/dois", valid_attributes, headers + expect(last_response.status).to eq(201) + end + end + + # context 'when the request is a large xml file' do + # let(:xml) { Base64.strict_encode64(file_fixture('large_file.xml').read) } + # let(:valid_attributes) do + # { + # "data" => { + # "type" => "dois", + # "attributes" => { + # "doi" => "10.14454/10703", + # "url" => "http://www.bl.uk/pdf/patspec.pdf", + # "xml" => xml, + # "event" => "publish" + # } + # } + # } + # end + + # before { post '/dois', valid_attributes.to_json, headers: headers } + + # it 'creates a Doi' do + # expect(json.dig('data', 'attributes', 'url')).to eq("http://www.bl.uk/pdf/patspec.pdf") + # expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") + + # expect(json.dig('data', 'attributes', 'titles')).to eq([{"title"=>"A dataset with a large file for testing purpose. Will be a but over 2.5 MB"}]) + # expect(json.dig('data', 'attributes', 'creators')).to eq([{"familyName"=>"Testing", "givenName"=>"Chris Baars At DANS For", "name"=>"Chris Baars At DANS For Testing", "type"=>"Person"}]) + # expect(json.dig('data', 'attributes', 'publisher')).to eq("DANS/KNAW") + # expect(json.dig('data', 'attributes', 'publicationYear')).to eq(2018) + # expect(json.dig('data', 'attributes', 'schemaVersion')).to eq("http://datacite.org/schema/kernel-4") + # expect(json.dig('data', 'attributes', 'types')).to eq("bibtex"=>"misc", "citeproc"=>"dataset", "resourceType"=>"Dataset", "resourceTypeGeneral"=>"Dataset", "ris"=>"DATA", "schemaOrg"=>"Dataset") + + # 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 + + # it 'returns status code 201' do + # expect(response).to have_http_status(201) + # end + # end + + context "when the request uses namespaced xml" do + let(:xml) { Base64.strict_encode64(file_fixture("ns0.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "event" => "publish", + }, + }, + } + end + + it "returns an error that schema is no longer supported" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(422) + expect(json.fetch("errors", nil)).to eq([{ "source" => "xml", "title" => "DOI 10.14454/10703: Schema http://datacite.org/schema/kernel-2.2 is no longer supported", "uid" => "10.14454/10703" }]) + end + end + + context "when the request uses schema 4.0" do + let(:xml) { Base64.strict_encode64(file_fixture("schema_4.0.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "event" => "publish", + }, + }, + } + end + + it "creates a Doi" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Southern Sierra Critical Zone Observatory (SSCZO), Providence Creek meteorological data, soil moisture and temperature, snow depth and air temperature" }]) + expect(json.dig("data", "attributes", "schemaVersion")).to eq("http://datacite.org/schema/kernel-4") + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + end + + context "when the request uses schema 4.2" do + let(:xml) { Base64.strict_encode64(file_fixture("schema_4.2.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "event" => "publish", + }, + }, + } + end + + it "creates a Doi" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Southern Sierra Critical Zone Observatory (SSCZO), Providence Creek meteorological data, soil moisture and temperature, snow depth and air temperature" }]) + expect(json.dig("data", "attributes", "schemaVersion")).to eq("http://datacite.org/schema/kernel-4") + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + end + + context "when the request uses namespaced xml" do + let(:xml) { Base64.strict_encode64(file_fixture("ns0.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "event" => "publish", + }, + }, + } + end + + it "returns an error that schema is no longer supported" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(422) + expect(json.fetch("errors", nil)).to eq([{ "source" => "xml", "title" => "DOI 10.14454/10703: Schema http://datacite.org/schema/kernel-2.2 is no longer supported", "uid" => "10.14454/10703" }]) + end + 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(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "source" => "test", + "titles" => titles, + "event" => "publish", + }, + }, + } + end + + it "creates a Doi" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq("title" => "Referee report. For: RESEARCH-3482 [version 5; referees: 1 approved, 1 approved with reservations]") + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/patspec.pdf") + expect(json.dig("data", "attributes", "source")).to eq("test") + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + end + + context "when the url changes ftp url" do + let(:url) { "ftp://ftp.library.noaa.gov/noaa_documents.lib/NOS/NGS/TM_NOS_NGS/TM_NOS_NGS_72.pdf" } + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => url, + "xml" => xml, + "event" => "publish", + }, + }, + } + end + + it "creates a Doi" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "url")).to eq(url) + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + end + + context "when the titles changes to nil" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "titles" => nil, + "event" => "publish", + }, + }, + } + end + + it "creates a Doi" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Eating your own Dog Food" }]) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/patspec.pdf") + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + end + + context "when the titles changes to blank" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "titles" => nil, + "event" => "publish", + }, + }, + } + end + + it "creates a Doi" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Eating your own Dog Food" }]) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/patspec.pdf") + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + end + + context "when the creators change" do + let(:creators) { [{ "affiliation" => [], "nameIdentifiers" => [], "name" => "Ollomi, Benjamin" }, { "affiliation" => [], "nameIdentifiers" => [], "name" => "Duran, Patrick" }] } + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "creators" => creators, + "event" => "publish", + }, + }, + } + end + + it "creates a Doi" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "creators")).to eq(creators) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/patspec.pdf") + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + end + + context "when doi has unpermitted characters" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/107+03", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "source" => "test", + "event" => "publish", + }, + }, + } + end + + it "returns validation error" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(422) + expect(json.dig("errors")).to eq([{ "source" => "doi", "title" => "Is invalid", "uid" => "10.14454/107+03" }]) + end + end + + context "creators no xml" do + let(:creators) { [{ "name" => "Ollomi, Benjamin" }, { "name" => "Duran, Patrick" }] } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => nil, + "creators" => creators, + "event" => "publish", + }, + }, + } + end + + it "returns validation error" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(422) + expect(json.dig("errors")).to eq([{ "source" => "metadata", "title" => "Is invalid", "uid" => "10.14454/10703" }, { "source" => "metadata", "title" => "Is invalid", "uid" => "10.14454/10703" }]) + end + end + + context "draft doi no url" do + let(:prefix) { create(:prefix, uid: "10.14454") } + let!(:client_prefix) { create(:client_prefix, client: client, prefix: prefix) } + + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10704", + }, + }, + } + end + + it "creates a Doi" do + post "/v3/dois", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10704") + expect(json.dig("data", "attributes", "state")).to eq("draft") + end + end + + context "when the request is invalid" do + let(:not_valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.aaaa03", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + }, + }, + } + end + + it "returns a validation failure message" do + post "/v3/dois", not_valid_attributes, headers + + expect(last_response.status).to eq(403) + expect(json["errors"]).to eq([{ "status" => "403", "title" => "You are not authorized to access this resource." }]) + end + end + + context "when the xml is invalid draft doi" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite_missing_creator.xml").read) } + let(:not_valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + }, + }, + } + end + + it "creates a Doi" do + post "/v3/dois", not_valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Eating your own Dog Food" }]) + expect(json.dig("data", "attributes", "url")).to eq("http://www.bl.uk/pdf/patspec.pdf") + expect(json.dig("data", "attributes", "creators")).to be_blank + end + end + + context "when the xml is invalid" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite_missing_creator.xml").read) } + let(:not_valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/4K3M-NYVG", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "event" => "publish", + }, + }, + } + end + + it "returns a validation failure message" do + post "/v3/dois", not_valid_attributes, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq([{ "source" => "creators", "title" => "DOI 10.14454/4k3m-nyvg: Missing child element(s). Expected is ( {http://datacite.org/schema/kernel-4}creator ). at line 4, column 0", "uid" => "10.14454/4k3m-nyvg" }]) + end + end + + describe "POST /dois/validate" do + context "validates" do + let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("datacite.xml"))) } + let(:params) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "xml" => xml, + }, + }, + } + end + + it "validates a Doi" do + post "/v3/dois/validate", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Eating your own Dog Food" }]) + expect(json.dig("data", "attributes", "dates")).to eq([{ "date" => "2016-12-20", "dateType" => "Created" }, { "date" => "2016-12-20", "dateType" => "Issued" }, { "date" => "2016-12-20", "dateType" => "Updated" }]) + end + end + + context "validatation fails with unpermitted characters in new DOI" do + let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("datacite.xml"))) } + let(:params) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/107+03", + "xml" => xml, + }, + }, + } + end + + it "returns validation error" do + post "/v3/dois/validate", params, headers + + expect(json.dig("errors")).to eq([{ "source" => "doi", "title" => "Is invalid", "uid" => "10.14454/107+03" }]) + end + end + + context "validates schema 3" do + let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("datacite_schema_3.xml"))) } + let(:params) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "xml" => xml, + }, + }, + } + end + + it "validates a Doi" do + post "/v3/dois/validate", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Data from: A new malaria agent in African hominids." }]) + expect(json.dig("data", "attributes", "dates")).to eq([{ "date" => "2011", "dateType" => "Issued" }]) + end + end + + context "when the creators are missing" do + let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("datacite_missing_creator.xml"))) } + let(:params) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "xml" => xml, + }, + }, + } + end + + it "validates a Doi" do + post "/v3/dois/validate", params, headers + + expect(last_response.status).to eq(200) + expect(json["errors"].size).to eq(1) + expect(json["errors"].first).to eq("source" => "creators", "title" => "DOI 10.14454/10703: Missing child element(s). Expected is ( {http://datacite.org/schema/kernel-4}creator ). at line 4, column 0", "uid" => "10.14454/10703") + end + end + + context "when the creators are malformed" do + let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("datacite_malformed_creator.xml"))) } + let(:params) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "xml" => xml, + }, + }, + } + end + + it "validates a Doi" do + post "/v3/dois/validate", params, headers + + expect(last_response.status).to eq(200) + expect(json["errors"].size).to eq(1) + expect(json["errors"].first).to eq("source" => "creatorName", "title" => "DOI 10.14454/10703: This element is not expected. Expected is ( {http://datacite.org/schema/kernel-4}affiliation ). at line 16, column 0", "uid" => "10.14454/10703") + end + end + + context "when attribute type names are wrong" do + let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("datacite_malformed_creator_name_type.xml"))) } + let(:params) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "xml" => xml, + }, + }, + } + end + + it "validates types are in right format" do + post "/v3/dois/validate", params, headers + + expect(last_response.status).to eq(200) + expect(json["errors"].first).to eq("source" => "creatorName', attribute 'nameType", "title" => "DOI 10.14454/10703: [facet 'enumeration'] The value 'personal' is not an element of the set {'Organizational', 'Personal'}. at line 12, column 0", "uid" => "10.14454/10703") + end + end + + context "validates citeproc" do + let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("citeproc.json"))) } + let(:params) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "xml" => xml, + }, + }, + } + end + + it "validates a Doi" do + post "/v3/dois/validate", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Eating your own Dog Food" }]) + expect(json.dig("data", "attributes", "dates")).to eq([{ "date" => "2016-12-20", "dateType" => "Issued" }]) + end + end + + context "validates codemeta" do + let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("codemeta.json"))) } + let(:params) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "xml" => xml, + }, + }, + } + end + + it "validates a Doi" do + post "/v3/dois/validate", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "R Interface to the DataONE REST API" }]) + expect(json.dig("data", "attributes", "dates")).to eq([{ "date" => "2016-05-27", "dateType" => "Issued" }, { "date" => "2016-05-27", "dateType" => "Created" }, { "date" => "2016-05-27", "dateType" => "Updated" }]) + end + end + + context "validates crosscite" do + let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("crosscite.json"))) } + let(:params) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "xml" => xml, + }, + }, + } + end + + it "validates a Doi" do + post "/v3/dois/validate", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Analysis Tools for Crossover Experiment of UI using Choice Architecture" }]) + expect(json.dig("data", "attributes", "dates")).to eq([{ "date" => "2016-03-27", "dateType" => "Issued" }]) + end + end + + context "validates bibtex" do + let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("crossref.bib"))) } + let(:params) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "xml" => xml, + }, + }, + } + end + + it "validates a Doi" do + post "/v3/dois/validate", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth" }]) + expect(json.dig("data", "attributes", "dates")).to eq([{ "date" => "2014", "dateType" => "Issued" }]) + end + end + + context "validates ris" do + let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("crossref.ris"))) } + let(:params) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "xml" => xml, + }, + }, + } + end + + it "validates a Doi" do + post "/v3/dois/validate", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth" }]) + expect(json.dig("data", "attributes", "dates")).to eq([{ "date" => "2014", "dateType" => "Issued" }]) + end + end + + context "validates crossref xml" do + let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("crossref.xml"))) } + let(:params) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "xml" => xml, + }, + }, + } + end + + it "validates a Doi" do + post "/v3/dois/validate", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Automated quantitative histology reveals vascular morphodynamics during Arabidopsis hypocotyl secondary growth" }]) + expect(json.dig("data", "attributes", "dates")).to eq([{ "date" => "2014-02-11", "dateType" => "Issued" }, { "date" => "2018-08-23T13:41:49Z", "dateType" => "Updated" }]) + end + end + + context "validates schema.org" do + let(:xml) { ::Base64.strict_encode64(File.read(file_fixture("schema_org.json"))) } + let(:params) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "xml" => xml, + }, + }, + } + end + + it "validates a Doi" do + post "/v3/dois/validate", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Eating your own Dog Food" }]) + expect(json.dig("data", "attributes", "dates")).to eq([{ "date" => "2016-12-20", "dateType" => "Issued" }, { "date" => "2016-12-20", "dateType" => "Created" }, { "date" => "2016-12-20", "dateType" => "Updated" }]) + end + end + end + + context "update individual attribute" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite_schema_3.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "xml" => xml, + "source" => "test", + "event" => "publish", + }, + }, + } + end + let(:update_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "schemaVersion" => "http://datacite.org/schema/kernel-4", + "regenerate" => true, + }, + }, + } + end + + it "creates a Doi" do + post "/v3/dois", valid_attributes, headers + + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Data from: A new malaria agent in African hominids." }]) + expect(json.dig("data", "attributes", "schemaVersion")).to eq("http://datacite.org/schema/kernel-3") + + doc = Nokogiri::XML(Base64.decode64(json.dig("data", "attributes", "xml")), nil, "UTF-8", &:noblanks) + expect(doc.collect_namespaces).to eq("xmlns" => "http://datacite.org/schema/kernel-3", "xmlns:dim" => "http://www.dspace.org/xmlns/dspace/dim", "xmlns:dryad" => "http://purl.org/dryad/terms/", "xmlns:dspace" => "http://www.dspace.org/xmlns/dspace/dim", "xmlns:mets" => "http://www.loc.gov/METS/", "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance") + end + + # it 'updates to schema 4.0' do + # put "/dois/10.14454/10703", update_attributes.to_json, headers: headers + + # expect(json.dig('data', 'attributes', 'doi')).to eq("10.14454/10703") + # expect(json.dig('data', 'attributes', 'schemaVersion')).to eq("http://datacite.org/schema/kernel-4") + + # doc = Nokogiri::XML(Base64.decode64(json.dig('data', 'attributes', 'xml')), nil, 'UTF-8', &:noblanks) + # expect(doc.collect_namespaces).to eq("xmlns"=>"http://datacite.org/schema/kernel-4", "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance") + # end + end + + context "mds doi" do + let(:xml) { Base64.strict_encode64(file_fixture("datacite_schema_3.xml").read) } + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "url" => "http://www.bl.uk/pdf/patspec.pdf", + "should_validate" => "true", + "source" => "mds", + "event" => "publish", + }, + }, + } + end + + let(:update_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "should_validate" => "true", + "xml" => xml, + "source" => "mds", + "event" => "show", + }, + }, + } + end + + it "add metadata" do + put "/v3/dois/10.14454/10703", update_attributes, headers + + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "schemaVersion")).to eq("http://datacite.org/schema/kernel-3") + + put "/dois/10.14454/10703", valid_attributes, headers + + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "schemaVersion")).to eq("http://datacite.org/schema/kernel-3") + expect(json.dig("data", "attributes", "titles")).to eq([{ "title" => "Data from: A new malaria agent in African hominids." }]) + end + end + + # Funder has been removed as valid contributor type in schema 4.0 + context "update contributor type with funder", elasticsearch: true do + let(:update_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "contributors" => [{ "contributorType" => "Funder", "name" => "Wellcome Trust", "nameType" => "Organizational" }], + }, + }, + } + end + + it "updates the Doi" do + patch "/v3/dois/#{doi.doi}", update_attributes, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq([{ "source" => "contributors", "title" => "Contributor type Funder is not supported in schema 4.", "uid" => "10.14454/4k3m-nyvg" }]) + end + end + + context "update rightsList", elasticsearch: true do + let(:rights_list) { [{ "rightsIdentifier" => "CC0-1.0" }] } + let(:update_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "rightsList" => rights_list, + }, + }, + } + end + + it "updates the Doi" do + patch "/v3/dois/#{doi.doi}", update_attributes, headers + + DataciteDoi.import + sleep 2 + + get "/v3/dois", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(1) + expect(json.dig("data", 0, "attributes", "rightsList")).to eq([{ "rights" => "Creative Commons Zero v1.0 Universal", + "rightsIdentifier" => "cc0-1.0", + "rightsIdentifierScheme" => "SPDX", + "rightsUri" => "https://creativecommons.org/publicdomain/zero/1.0/legalcode", + "schemeUri" => "https://spdx.org/licenses/" }]) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("meta", "affiliations")).to eq([{ "count" => 1, "id" => "ror.org/04wxnsj81", "title" => "DataCite" }]) + expect(json.dig("meta", "licenses")).to eq([{ "count" => 1, "id" => "cc0-1.0", "title" => "CC0-1.0" }]) + end + end + + context "update rightsList with rightsUri", elasticsearch: true do + let(:rights_list) do + [{ + "rightsUri" => "https://creativecommons.org/publicdomain/zero/1.0/", + }] + end + let(:update_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "rightsList" => rights_list, + }, + }, + } + end + + it "updates the Doi" do + patch "/v3/dois/#{doi.doi}", update_attributes, headers + + DataciteDoi.import + sleep 2 + + get "/v3/dois", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(1) + expect(json.dig("data", 0, "attributes", "rightsList")).to eq(rights_list) + expect(json.dig("meta", "total")).to eq(1) + expect(json.dig("meta", "affiliations")).to eq([{ "count" => 1, "id" => "ror.org/04wxnsj81", "title" => "DataCite" }]) + # expect(json.dig('meta', 'licenses')).to eq([{"count"=>1, "id"=>"CC0-1.0", "title"=>"CC0-1.0"}]) + end + end + + context "update subjects" do + let(:subjects) do + [{ "subject" => "80505 Web Technologies (excl. Web Search)", + "schemeUri" => "http://www.abs.gov.au/ausstats/abs@.nsf/0/6BB427AB9696C225CA2574180004463E", + "subjectScheme" => "FOR", + "lang" => "en" }] + end + let(:update_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "subjects" => subjects, + }, + }, + } + end + + it "updates the Doi" do + patch "/v3/dois/#{doi.doi}", update_attributes, headers + + expect(json.dig("data", "attributes", "subjects")).to eq([{ "lang" => "en", + "subject" => "80505 Web Technologies (excl. Web Search)", + "subjectScheme" => "FOR" }, + { "schemeUri" => "http://www.oecd.org/science/inno/38235147.pdf", + "subject" => "FOS: Computer and information sciences", + "subjectScheme" => "Fields of Science and Technology (FOS)" }]) + end + end + + context "update contentUrl" do + let(:content_url) { ["s3://cgp-commons-public/topmed_open_access/197bc047-e917-55ed-852d-d563cdbc50e4/NWD165827.recab.cram", "gs://topmed-irc-share/public/NWD165827.recab.cram"] } + let(:update_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "contentUrl" => content_url, + }, + }, + } + end + + it "updates the Doi" do + patch "/v3/dois/#{doi.doi}", update_attributes, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "contentUrl")).to eq(content_url) + end + end + + context "update multiple affiliations" do + let(:creators) { [{ "name" => "Ollomi, Benjamin", "affiliation" => [{ "name" => "Cambridge University" }, { "name" => "EMBL-EBI" }], "nameIdentifiers" => [] }] } + let(:update_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "creators" => creators, + }, + }, + } + end + + it "updates the Doi" do + patch "/v3/dois/#{doi.doi}?affiliation=true", update_attributes, headers + + expect(json.dig("data", "attributes", "creators")).to eq(creators) + + doc = Nokogiri::XML(Base64.decode64(json.dig("data", "attributes", "xml")), nil, "UTF-8", &:noblanks) + expect(doc.at_css("creators", "creator").to_s + "\n").to eq( + <<~HEREDOC, + + + Ollomi, Benjamin + Cambridge University + EMBL-EBI + + + HEREDOC + ) + end + end + + # TODO needs updating + # context 'update geoLocationPoint' do + # let(:geo_locations) { [ + # { + # "geoLocationPoint" => { + # "pointLatitude" => "49.0850736", + # "pointLongitude" => "-123.3300992" + # } + # }] } + # let(:update_attributes) do + # { + # "data" => { + # "type" => "dois", + # "attributes" => { + # "geoLocations" => geo_locations + # } + # } + # } + # end + + # it 'updates the Doi' do + # patch "/dois/#{doi.doi}", update_attributes, headers + + # expect(last_response.status).to eq(200) + # expect(json.dig('data', 'attributes', 'geoLocations')).to eq(geo_locations) + + # doc = Nokogiri::XML(Base64.decode64(json.dig('data', 'attributes', 'xml')), nil, 'UTF-8', &:noblanks) + # expect(doc.at_css("geoLocations", "geoLocation").to_s + "\n").to eq( + # <<~HEREDOC + # + # + # + # 49.0850736 + # -123.3300992 + # + # + # + # HEREDOC + # ) + # end + # end + + context "remove series_information" do + let(:xml) { File.read(file_fixture("datacite_series_information.xml")) } + let(:descriptions) do + [{ "description" => "Axel is a minimalistic cliff climbing rover that can explore + extreme terrains from the moon, Mars, and beyond. To + increase the technology readiness and scientific usability + of Axel, a sampling system needs to be designed and + build for sampling different rock and soils. To decrease + the amount of force required to sample clumpy and + possibly icy science targets, a percussive scoop could be + used. A percussive scoop uses repeated impact force to + dig into samples and a rotary actuation to collect the + samples. Percussive scooping can reduce the amount of downward force required by about two to four + times depending on the cohesion of the soil and the depth of the sampling. The goal for this project is to + build a working prototype of a percussive scoop for Axel.", "descriptionType" => "Abstract" }] + end + let(:doi) { create(:doi, client: client, doi: "10.14454/05mb-q396", url: "https://example.org", xml: xml, event: "publish") } + let(:update_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "descriptions" => descriptions, + }, + }, + } + end + + it "updates the Doi" do + patch "/v3/dois/#{doi.doi}", update_attributes, headers + + expect(json.dig("data", "attributes", "descriptions")).to eq(descriptions) + expect(json.dig("data", "attributes", "container")).to be_empty + end + end + + context "remove series_information via xml", elasticsearch: true do + let(:xml) { Base64.strict_encode64(File.read(file_fixture("datacite_series_information.xml"))) } + let(:xml_new) { Base64.strict_encode64(File.read(file_fixture("datacite_no_series_information.xml"))) } + let!(:doi) { create(:doi, client: client, doi: "10.14454/05mb-q396", url: "https://datacite.org", event: "publish") } + let(:update_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "xml" => xml, + }, + }, + } + end + let(:update_attributes_again) do + { + "data" => { + "type" => "dois", + "attributes" => { + "xml" => xml_new, + }, + }, + } + end + + before do + DataciteDoi.import + sleep 2 + end + + it "updates the Doi" do + get "/v3/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", "container")).to be_empty + + patch "/v3/dois/#{doi.doi}", update_attributes, headers + + expect(json.dig("data", "attributes", "descriptions").size).to eq(2) + expect(json.dig("data", "attributes", "titles", 0, "title")).to eq("Percussive Scoop Sampling in Extreme Terrain") + expect(json.dig("data", "attributes", "descriptions").last).to eq("description" => "Keck Institute for Space Studies", "descriptionType" => "SeriesInformation") + expect(json.dig("data", "attributes", "container")).to eq("title" => "Keck Institute for Space Studies", "type" => "Series") + + patch "/v3/dois/#{doi.doi}", update_attributes_again, headers + + expect(json.dig("data", "attributes", "descriptions").size).to eq(1) + expect(json.dig("data", "attributes", "container")).to be_empty + end + end + + context "landing page" do + let(:url) { "https://blog.datacite.org/re3data-science-europe/" } + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:landing_page) do + { + "checked" => Time.zone.now.utc.iso8601, + "status" => 200, + "url" => url, + "contentType" => "text/html", + "error" => nil, + "redirectCount" => 0, + "redirectUrls" => [], + "downloadLatency" => 200, + "hasSchemaOrg" => true, + "schemaOrgId" => "10.14454/10703", + "dcIdentifier" => nil, + "citationDoi" => nil, + "bodyHasPid" => true, + } + end + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => url, + "xml" => xml, + "landingPage" => landing_page, + "event" => "publish", + }, + }, + } + end + + it "creates a doi" do + post "/v3/dois", valid_attributes.to_json, { "HTTP_ACCEPT" => "application/vnd.api+json", "CONTENT_TYPE" => "application/vnd.api+json", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "url")).to eq(url) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "landingPage")).to eq(landing_page) + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + + it "fails to create a doi with bad data" do + valid_attributes["data"]["attributes"]["landingPage"] = "http://example.com" + post "/v3/dois", valid_attributes.to_json, { "HTTP_ACCEPT" => "application/vnd.api+json", "CONTENT_TYPE" => "application/vnd.api+json", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(422) + end + end + + context "update with landing page info as admin" do + let(:url) { "https://blog.datacite.org/re3data-science-europe/" } + let(:doi) { create(:doi, doi: "10.14454/10703", url: url, client: client) } + let(:landing_page) do + { + "checked" => Time.zone.now.utc.iso8601, + "status" => 200, + "url" => url, + "contentType" => "text/html", + "error" => nil, + "redirectCount" => 0, + "redirectUrls" => [], + "downloadLatency" => 200, + "hasSchemaOrg" => true, + "schemaOrgId" => "10.14454/10703", + "dcIdentifier" => nil, + "citationDoi" => nil, + "bodyHasPid" => true, + } + end + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "landingPage" => landing_page, + "event" => "publish", + }, + }, + } + end + + it "creates a doi" do + put "/v3/dois/#{doi.doi}", valid_attributes.to_json, { "HTTP_ACCEPT" => "application/vnd.api+json", "CONTENT_TYPE" => "application/vnd.api+json", "HTTP_AUTHORIZATION" => "Bearer " + admin_bearer } + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "landingPage")).to eq(landing_page) + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + end + + context "landing page schema-org-id array" do + let(:url) { "https://blog.datacite.org/re3data-science-europe/" } + let(:xml) { Base64.strict_encode64(file_fixture("datacite.xml").read) } + let(:landing_page) do + { + "checked" => Time.zone.now.utc.iso8601, + "status" => 200, + "url" => url, + "contentType" => "text/html", + "error" => nil, + "redirectCount" => 0, + "redirectUrls" => [], + "downloadLatency" => 200, + "hasSchemaOrg" => true, + "schemaOrgId" => [ + "http://dx.doi.org/10.4225/06/564AB348340D5", + ], + "dcIdentifier" => nil, + "citationDoi" => nil, + "bodyHasPid" => true, + } + end + let(:valid_attributes) do + { + "data" => { + "type" => "dois", + "attributes" => { + "doi" => "10.14454/10703", + "url" => url, + "xml" => xml, + "landingPage" => landing_page, + "event" => "publish", + }, + }, + } + end + + it "creates a Doi" do + post "/v3/dois", valid_attributes.to_json, { "HTTP_ACCEPT" => "application/vnd.api+json", "CONTENT_TYPE" => "application/vnd.api+json", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(201) + expect(json.dig("data", "attributes", "url")).to eq(url) + expect(json.dig("data", "attributes", "doi")).to eq("10.14454/10703") + expect(json.dig("data", "attributes", "landingPage")).to eq(landing_page) + expect(json.dig("data", "attributes", "state")).to eq("findable") + end + end + end + + describe "DELETE /v3/dois/:id" do + let(:doi) { create(:doi, client: client, aasm_state: "draft") } + + it "returns status code 204" do + delete "/v3/dois/#{doi.doi}", nil, headers + + expect(last_response.status).to eq(204) + expect(last_response.body).to be_empty + end + end + + describe "DELETE /v3/dois/:id findable state" do + let(:doi) { create(:doi, client: client, aasm_state: "findable") } + + it "returns status code 405" do + delete "/v3/dois/#{doi.doi}", nil, headers + + expect(last_response.status).to eq(405) + expect(json["errors"]).to eq([{ "status" => "405", "title" => "Method not allowed" }]) + end + end + + describe "POST /v3/dois/set-url", elasticsearch: true do + let!(:dois) { create_list(:doi, 3, client: client, url: nil) } + + it "returns dois" do + post "/v3/dois/set-url", nil, admin_headers + + expect(last_response.status).to eq(200) + expect(json["message"]).to eq("Adding missing URLs queued.") + end + end + + describe "GET /v3/dois/random" do + it "returns random doi" do + get "/v3/dois/random?prefix=10.14454", headers: headers + + expect(last_response.status).to eq(200) + expect(json["dois"].first).to start_with("10.14454") + end + end + + describe "GET /v3/dois/ linkcheck results", elasticsearch: true do + let(:landing_page) do + { + "checked" => Time.zone.now.utc.iso8601, + "status" => 200, + "url" => "http://example.com", + "contentType" => "text/html", + "error" => nil, + "redirectCount" => 0, + "redirectUrls" => [], + "downloadLatency" => 200, + "hasSchemaOrg" => true, + "schemaOrgId" => "10.14454/10703", + "dcIdentifier" => nil, + "citationDoi" => nil, + "bodyHasPid" => true, + } + end + + # Setup an initial DOI with results will check permissions against. + let!(:doi) do + create(:doi, doi: "10.24425/2210181332", + client: client, + state: "findable", + event: "publish", + landing_page: landing_page) + end + + # Create a different dummy client and a doi with entry associated + # This is so we can test clients accessing others information + let(:other_client) { create(:client, provider: provider, symbol: "DATACITE.DNE", password: "notarealpassword") } + let(:other_doi) do + create(:doi, doi: "10.24425/2210181332", + client: other_client, + state: "findable", + event: "publish", + landing_page: landing_page) + end + + before do + DataciteDoi.import + sleep 2 + end + + context "anonymous get" do + let(:headers) { { "HTTP_ACCEPT" => "application/vnd.api+json" } } + + it "returns without landing page results" do + get "/v3/dois/#{doi.doi}", nil, headers + + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi) + expect(json.dig("data", "attributes", "landingPage")).to eq(nil) + end + end + + context "client authorised get own dois" do + let(:bearer) { User.generate_token(role_id: "client_admin", client_id: client.symbol.downcase) } + let(:headers) { { "HTTP_ACCEPT" => "application/vnd.api+json", "HTTP_AUTHORIZATION" => "Bearer " + bearer } } + + it "returns with landing page results" do + get "/v3/dois/#{doi.doi}", nil, headers + + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi) + # expect(json.dig('data', 'attributes', 'landingPage')).to eq(landing_page) + end + end + + context "client authorised try get diff dois landing data" do + let(:bearer) { User.generate_token(role_id: "client_admin", client_id: client.symbol.downcase) } + let(:headers) { { "HTTP_ACCEPT" => "application/vnd.api+json", "HTTP_AUTHORIZATION" => "Bearer " + bearer } } + + it "returns with landing page results" do + get "/v3/dois/#{other_doi.doi}", nil, headers + + expect(json.dig("data", "attributes", "doi")).to eq(other_doi.doi) + expect(json.dig("data", "attributes", "landingPage")).to eq(nil) + end + end + + context "authorised staff admin read" do + let(:bearer) { User.generate_token(role_id: "client_admin", client_id: client.symbol.downcase) } + let(:headers) { { "HTTP_ACCEPT" => "application/vnd.api+json", "HTTP_AUTHORIZATION" => "Bearer " + admin_bearer } } + + it "returns with landing page results" do + get "/v3/dois/#{doi.doi}", nil, headers + + expect(json.dig("data", "attributes", "doi")).to eq(doi.doi) + expect(json.dig("data", "attributes", "landingPage")).to eq(landing_page) + end + end + end + + describe "GET /dois/random?prefix" do + it "returns random doi with prefix" do + get "/v3/dois/random?prefix=#{prefix.uid}", nil, headers + + expect(last_response.status).to eq(200) + expect(json["dois"].first).to start_with("10.14454") + end + end + + describe "GET /v3/dois/random?number" do + let(:number) { 122149076 } + + it "returns predictable doi" do + get "/v3/dois/random?prefix=10.14454&number=#{number}", nil, headers + + expect(last_response.status).to eq(200) + expect(json["dois"].first).to eq("10.14454/3mfp-6m52") + end + end + + describe "GET /v3/dois/DOI/get-url", vcr: true, elasticsearch: true do + context "it works" do + let!(:doi) { create(:doi, client: client, doi: "10.5438/fj3w-0shd", url: "https://blog.datacite.org/data-driven-development/", event: "publish") } + + before do + DataciteDoi.import + sleep 2 + end + + it "returns url" do + get "/v3/dois/#{doi.doi}/get-url", nil, headers + + expect(json["url"]).to eq("https://blog.datacite.org/data-driven-development/") + expect(last_response.status).to eq(200) + end + end + + context "no password" do + let!(:doi) { create(:doi, client: client, doi: "10.14454/05mb-q396", event: "publish") } + + before do + DataciteDoi.import + sleep 2 + end + + it "returns url" do + get "/v3/dois/#{doi.doi}/get-url", nil, headers + + expect(json["url"]).to eq("https://example.org") + expect(last_response.status).to eq(200) + end + end + + context "not found" do + let!(:datacite_doi) { create(:doi, client: client, doi: "10.14454/61y1-e521", event: "publish", type: "DataciteDoi") } + + before do + DataciteDoi.import + sleep 2 + end + + it "returns not found" do + get "/v3/dois/#{datacite_doi.doi}/get-url", nil, headers + + expect(last_response.status).to eq(404) + expect(json["errors"]).to eq([{ "status" => 404, "title" => "Not found" }]) + end + end + + context "draft doi" do + let!(:doi) { create(:doi, client: client, doi: "10.14454/61y1-e521") } + + before do + DataciteDoi.import + sleep 2 + end + + it "returns not found" do + get "/v3/dois/#{doi.doi}/get-url", nil, headers + + expect(last_response.status).to eq(200) + expect(json["url"]).to eq(doi.url) + end + end + + # context 'not DataCite DOI' do + # let(:doi) { create(:doi, client: client, doi: "10.1371/journal.pbio.2001414", event: "publish") } + + # it 'returns nil' do + # get "/dois/#{doi.doi}/get-url", nil, headers + + # expect(last_response.status).to eq(403) + # expect(json).to eq("errors"=>[{"status"=>403, "title"=>"SERVER NOT RESPONSIBLE FOR HANDLE"}]) + # end + # end + end + + describe "GET /v3/dois/get-dois", vcr: true do + let(:prefix) { create(:prefix, uid: "10.5438") } + let!(:client_prefix) { create(:client_prefix, prefix: prefix, client: client) } + + it "returns all dois" do + get "/v3/dois/get-dois", nil, headers + + expect(last_response.status).to eq(200) + expect(json["dois"].length).to eq(449) + expect(json["dois"].first).to eq("10.5438/0000-00SS") + end + end + + describe "GET /v3/dois/get-dois no authentication", vcr: true do + it "returns error message" do + get "/v3/dois/get-dois" + + expect(last_response.status).to eq(401) + expect(json["errors"]).to eq([{ "status" => "401", "title" => "Bad credentials." }]) + end + end + + describe "content_negotation", elasticsearch: true do + let(:provider) { create(:provider, symbol: "DATACITE") } + 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!(:datacite_doi) { create(:doi, client: client, aasm_state: "findable", type: "DataciteDoi") } + + before do + DataciteDoi.import + sleep 2 + end + + context "no permission" do + let(:datacite_doi) { create(:doi) } + + it "returns error message" do + get "/v3/dois/#{datacite_doi.doi}", nil, { "HTTP_ACCEPT" => "application/vnd.jats+xml", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(404) + expect(json).to eq("errors" => [{ "status" => "404", "title" => "The resource you are looking for doesn't exist." }]) + end + end + + context "no authentication" do + let(:datacite_doi) { create(:doi) } + + it "returns error message" do + get "/v3/dois/#{datacite_doi.doi}", nil, { "HTTP_ACCEPT" => "application/vnd.jats+xml" } + + expect(last_response.status).to eq(404) + expect(json).to eq("errors" => [{ "status" => "404", "title" => "The resource you are looking for doesn't exist." }]) + end + end + + context "application/vnd.jats+xml" do + it "returns the Doi" do + get "/v3/dois/#{datacite_doi.doi}", nil, { "HTTP_ACCEPT" => "application/vnd.jats+xml", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(200) + jats = Maremma.from_xml(last_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 + end + + context "application/vnd.jats+xml link" do + it "returns the Doi" do + get "/v3/dois/application/vnd.jats+xml/#{datacite_doi.doi}" + + expect(last_response.status).to eq(200) + jats = Maremma.from_xml(last_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 + end + + context "application/vnd.datacite.datacite+xml" do + it "returns the Doi" do + get "/v3/dois/#{datacite_doi.doi}", nil, { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+xml", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(200) + data = Maremma.from_xml(last_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 + end + + context "application/vnd.datacite.datacite+xml link" do + it "returns the Doi" do + get "/v3/dois/application/vnd.datacite.datacite+xml/#{datacite_doi.doi}" + + expect(last_response.status).to eq(200) + data = Maremma.from_xml(last_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 + end + + context "application/vnd.datacite.datacite+xml schema 3" do + let(:xml) { file_fixture("datacite_schema_3.xml").read } + let(:datacite_doi) { create(:doi, xml: xml, client: client, regenerate: false) } + + it "returns the Doi" do + get "/v3/dois/#{datacite_doi.doi}", nil, { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+xml", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(200) + data = Maremma.from_xml(last_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 + 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", 'HTTP_AUTHORIZATION' => 'Bearer ' + bearer } } + + # it 'returns the Doi' do + # expect(last_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 + it "returns error message" do + get "/v3/dois/xxx", nil, { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+xml", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(404) + expect(json["errors"]).to eq([{ "status" => "404", "title" => "The resource you are looking for doesn't exist." }]) + end + end + + context "application/vnd.datacite.datacite+json" do + it "returns the Doi" do + get "/v3/dois/#{datacite_doi.doi}", nil, { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+json", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(200) + expect(json["doi"]).to eq(datacite_doi.doi) + end + end + + context "application/vnd.datacite.datacite+json link" do + it "returns the Doi" do + get "/v3/dois/application/vnd.datacite.datacite+json/#{datacite_doi.doi}" + + expect(last_response.status).to eq(200) + expect(json["doi"]).to eq(datacite_doi.doi) + end + end + + context "application/vnd.crosscite.crosscite+json" do + it "returns the Doi" do + get "/v3/dois/#{datacite_doi.doi}", nil, { "HTTP_ACCEPT" => "application/vnd.crosscite.crosscite+json", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(200) + expect(json["doi"]).to eq(datacite_doi.doi) + end + end + + context "application/vnd.crosscite.crosscite+json link" do + it "returns the Doi" do + get "/v3/dois/application/vnd.crosscite.crosscite+json/#{datacite_doi.doi}" + + expect(last_response.status).to eq(200) + expect(json["doi"]).to eq(datacite_doi.doi) + end + end + + context "application/vnd.schemaorg.ld+json" do + it "returns the Doi" do + get "/v3/dois/#{datacite_doi.doi}", nil, { "HTTP_ACCEPT" => "application/vnd.schemaorg.ld+json", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(200) + expect(json["@type"]).to eq("Dataset") + end + end + + context "application/vnd.schemaorg.ld+json link" do + it "returns the Doi" do + get "/v3/dois/application/vnd.schemaorg.ld+json/#{datacite_doi.doi}" + + expect(last_response.status).to eq(200) + expect(json["@type"]).to eq("Dataset") + end + end + + context "application/ld+json" do + it "returns the Doi" do + get "/v3/dois/#{datacite_doi.doi}", nil, { "HTTP_ACCEPT" => "application/ld+json", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(200) + expect(json["@type"]).to eq("Dataset") + end + end + + context "application/ld+json link" do + it "returns the Doi" do + get "/v3/dois/application/ld+json/#{datacite_doi.doi}" + + expect(last_response.status).to eq(200) + expect(json["@type"]).to eq("Dataset") + end + end + + context "application/vnd.citationstyles.csl+json" do + it "returns the Doi" do + get "/v3/dois/#{datacite_doi.doi}", nil, { "HTTP_ACCEPT" => "application/vnd.citationstyles.csl+json", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(200) + expect(json["type"]).to eq("dataset") + end + end + + context "application/vnd.citationstyles.csl+json link" do + it "returns the Doi" do + get "/v3/dois/application/vnd.citationstyles.csl+json/#{datacite_doi.doi}" + + expect(last_response.status).to eq(200) + expect(json["type"]).to eq("dataset") + end + end + + context "application/x-research-info-systems" do + it "returns the Doi" do + get "/v3/dois/#{datacite_doi.doi}", nil, { "HTTP_ACCEPT" => "application/x-research-info-systems", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(200) + expect(last_response.body).to start_with("TY - DATA") + end + end + + context "application/x-research-info-systems link" do + it "returns the Doi" do + get "/v3/dois/application/x-research-info-systems/#{datacite_doi.doi}" + + expect(last_response.status).to eq(200) + expect(last_response.body).to start_with("TY - DATA") + end + end + + context "application/x-bibtex" do + it "returns the Doi" do + get "/v3/dois/#{datacite_doi.doi}", nil, { "HTTP_ACCEPT" => "application/x-bibtex", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(200) + expect(last_response.body).to start_with("@misc{https://doi.org/#{datacite_doi.doi.downcase}") + end + end + + context "application/x-bibtex link" do + it "returns the Doi" do + get "/v3/dois/application/x-bibtex/#{datacite_doi.doi}" + + expect(last_response.status).to eq(200) + expect(last_response.body).to start_with("@misc{https://doi.org/#{datacite_doi.doi.downcase}") + end + end + + context "text/csv" do + it "returns the Doi" do + get "/v3/dois/#{datacite_doi.doi}", nil, { "HTTP_ACCEPT" => "text/csv", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(200) + expect(last_response.body).to include(datacite_doi.doi) + end + end + + context "text/csv link" do + it "returns the Doi" do + get "/v3/dois/text/csv/#{datacite_doi.doi}" + + expect(last_response.status).to eq(200) + expect(last_response.body).to include(datacite_doi.doi) + end + end + + context "text/x-bibliography" do + context "default style" do + it "returns the Doi" do + get "/v3/dois/#{datacite_doi.doi}", nil, { "HTTP_ACCEPT" => "text/x-bibliography", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(200) + expect(last_response.body).to start_with("Ollomo, B.") + end + end + + context "default style link" do + it "returns the Doi" do + get "/v3/dois/text/x-bibliography/#{datacite_doi.doi}" + + expect(last_response.status).to eq(200) + expect(last_response.body).to start_with("Ollomo, B.") + end + end + + context "ieee style" do + it "returns the Doi" do + get "/v3/dois/#{datacite_doi.doi}?style=ieee", nil, { "HTTP_ACCEPT" => "text/x-bibliography", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(200) + expect(last_response.body).to start_with("B. Ollomo") + end + end + + context "ieee style link" do + it "returns the Doi" do + get "/v3/dois/text/x-bibliography/#{datacite_doi.doi}?style=ieee" + + expect(last_response.status).to eq(200) + expect(last_response.body).to start_with("B. Ollomo") + end + end + + context "style and locale" do + it "returns the Doi" do + get "/v3/dois/#{datacite_doi.doi}?style=vancouver&locale=de", nil, { "HTTP_ACCEPT" => "text/x-bibliography", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(200) + expect(last_response.body).to start_with("Ollomo B") + end + end + end + + context "unknown content type" do + it "returns the Doi" do + get "/v3/dois/#{datacite_doi.doi}", nil, { "HTTP_ACCEPT" => "application/vnd.ms-excel", "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(406) + expect(json["errors"]).to eq([{ "status" => "406", "title" => "The content type is not recognized." }]) + end + end + + context "missing content type" do + it "returns the Doi" do + get "/v3/dois/#{datacite_doi.doi}", nil, { "HTTP_AUTHORIZATION" => "Bearer " + bearer } + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "doi")).to eq(datacite_doi.doi.downcase) + end + end + end +end diff --git a/spec/requests/v3/events_spec.rb b/spec/requests/v3/events_spec.rb new file mode 100644 index 000000000..b7ecb134a --- /dev/null +++ b/spec/requests/v3/events_spec.rb @@ -0,0 +1,1126 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe EventsController, type: :request, elasticsearch: true, vcr: true do + let(:provider) { create(:provider, symbol: "DATACITE") } + let(:client) do + create( + :client, + provider: provider, + symbol: ENV["MDS_USERNAME"], + password: ENV["MDS_PASSWORD"], + ) + end + + before(:each) do + allow(Time).to receive(:now).and_return(Time.mktime(2_015, 4, 8)) + allow(Time.zone).to receive(:now).and_return(Time.mktime(2_015, 4, 8)) + end + + let(:event) { build(:event) } + let(:errors) { [{ "status" => "401", "title" => "Bad credentials." }] } + + # Successful response from creating via the API. + let(:success) do + { + "id" => event.uuid, + "type" => "events", + "attributes" => { + "subjId" => "http://www.citeulike.org/user/dbogartoit", + "objId" => "http://doi.org/10.1371/journal.pmed.0030186", + "messageAction" => "create", + "sourceToken" => "citeulike_123", + "relationTypeId" => "bookmarks", + "sourceId" => "citeulike", + "total" => 1, + "license" => "https://creativecommons.org/publicdomain/zero/1.0/", + "occurredAt" => "2015-04-08T00:00:00.000Z", + "subj" => { + "@id" => "http://www.citeulike.org/user/dbogartoit", + "@type" => "CreativeWork", + "author" => [{ "givenName" => "dbogartoit" }], + "name" => "CiteULike bookmarks for user dbogartoit", + "publisher" => { "@type" => "Organization", "name" => "CiteULike" }, + "periodical" => { + "@type" => "Periodical", + "@id" => "https://doi.org/10.13039/100011326", + "name" => "CiteULike", + "issn" => "9812-847X", + }, + "funder" => { + "@type" => "Organization", + "@id" => "https://doi.org/10.13039/100011326", + "name" => "CiteULike", + }, + "version" => "1.0", + "proxyIdentifiers" => %w[10.13039/100011326], + "datePublished" => "2006-06-13T16:14:19Z", + "dateModified" => "2006-06-13T16:14:19Z", + "url" => "http://www.citeulike.org/user/dbogartoit", + }, + "obj" => {}, + }, + } + end + + let(:token) { User.generate_token(role_id: "staff_admin") } + let(:uuid) { SecureRandom.uuid } + let(:headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json; version=2", + "HTTP_AUTHORIZATION" => "Bearer #{token}", + } + end + + context "create" do + let(:uri) { "/v3/events" } + let(:params) do + { + "data" => { + "type" => "events", + "id" => event.uuid, + "attributes" => { + "subjId" => event.subj_id, + "subj" => event.subj, + "objId" => event.obj_id, + "relationTypeId" => event.relation_type_id, + "sourceId" => event.source_id, + "sourceToken" => event.source_token, + }, + }, + } + end + + context "as admin user" do + it "JSON" do + post uri, params, headers + + expect(last_response.status).to eq(201) + expect(json["errors"]).to be_nil + expect(json.dig("data", "id")).to eq(event.uuid) + # expect(json.dig("data", "relationships", "dois", "data")).to eq([{"id"=>"10.1371/journal.pmed.0030186", "type"=>"dois"}]) + end + end + + context "with very long url" do + let(:url) do + "http://navigator.eumetsat.int/soapservices/cswstartup?service=csw&version=2.0.2&request=getrecordbyid&outputschema=http%3A%2F%2Fwww.isotc211.org%2F2005%2Fgmd&id=eo%3Aeum%3Adat%3Amult%3Arac-m11-iasia" + end + let(:params) do + { + "data" => { + "type" => "events", + "attributes" => { + "subjId" => event.subj_id, + "subj" => event.subj, + "objId" => url, + "relationTypeId" => event.relation_type_id, + "sourceId" => "datacite-url", + "sourceToken" => event.source_token, + }, + }, + } + end + + it "JSON" do + post uri, params, headers + + expect(last_response.status).to eq(201) + expect(json["errors"]).to be_nil + expect(json.dig("data", "id")).not_to eq(event.uuid) + expect(json.dig("data", "attributes", "objId")).to eq(url) + end + end + + context "as staff user" do + let(:token) { User.generate_token(role_id: "staff_user") } + + it "JSON" do + post uri, params, headers + + expect(last_response.status).to eq(403) + expect(json["errors"]).to eq( + [ + { + "status" => "403", + "title" => "You are not authorized to access this resource.", + }, + ], + ) + expect(json["data"]).to be_nil + end + end + + context "as regular user" do + let(:token) { User.generate_token(role_id: "user") } + + it "JSON" do + post uri, params, headers + + expect(last_response.status).to eq(403) + expect(json["errors"]).to eq( + [ + { + "status" => "403", + "title" => "You are not authorized to access this resource.", + }, + ], + ) + expect(json["data"]).to be_blank + end + end + + context "without sourceToken" do + let(:params) do + { + "data" => { + "type" => "events", + "attributes" => { + "uuid" => uuid, + "subjId" => event.subj_id, + "sourceId" => event.source_id, + }, + }, + } + end + + it "JSON" do + post uri, params, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq( + [{ "status" => 422, "title" => "Source token can't be blank" }], + ) + expect(json["data"]).to be_nil + end + end + + context "without sourceId" do + let(:params) do + { + "data" => { + "type" => "events", + "attributes" => { + "uuid" => uuid, + "subjId" => event.subj_id, + "sourceToken" => event.source_token, + }, + }, + } + end + + it "JSON" do + post uri, params, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq( + [{ "status" => 422, "title" => "Source can't be blank" }], + ) + expect(json["data"]).to be_blank + end + end + + context "without subjId" do + let(:params) do + { + "data" => { + "type" => "events", + "attributes" => { + "uuid" => uuid, + "sourceId" => event.source_id, + "sourceToken" => event.source_token, + }, + }, + } + end + + it "JSON" do + post uri, params, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq( + [{ "status" => 422, "title" => "Subj can't be blank" }], + ) + expect(json["data"]).to be_blank + end + end + + context "with wrong API token" do + let(:headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json; version=2", + "HTTP_AUTHORIZATION" => "Bearer 12345678", + } + end + + it "JSON" do + post uri, params, headers + expect(last_response.status).to eq(401) + + expect(json["errors"]).to eq(errors) + expect(json["data"]).to be_blank + end + end + + context "with missing data param" do + let(:params) do + { + "event" => { + "type" => "events", + "attributes" => { "uuid" => uuid, "sourceToken" => "123" }, + }, + } + end + + it "JSON" do + post uri, params, headers + + expect(last_response.status).to eq(422) + expect(json.dig("errors", 0, "title")).to start_with("Invalid payload") + expect(json["data"]).to be_blank + end + end + + context "with params in wrong format" do + let(:params) do + { + "data" => + "10.1371/journal.pone.0036790 2012-05-15 New Dromaeosaurids (Dinosauria: Theropoda) from the Lower Cretaceous of Utah, and the Evolution of the Dromaeosaurid Tail", + } + end + + it "JSON" do + post uri, params, headers + + expect(last_response.status).to eq(422) + error = json["errors"].first + expect(error["status"]).to eq("422") + expect(error["title"]).to start_with("Invalid payload") + expect(json["data"]).to be_blank + end + end + + context "existing entry" do + let!(:event) { create(:event) } + + it "JSON" do + post uri, params, headers + + expect(last_response.status).to eq(200) + expect(json["errors"]).to be_nil + expect(json.dig("data", "id")).to eq(event.uuid) + # expect(json.dig("data", "relationships", "dois", "data")).to eq([{"id"=>"10.1371/journal.pmed.0030186", "type"=>"dois"}]) + end + end + + context "with registrant information" do + let(:uri) { "/v3/events" } + let(:params) do + { + "data" => { + "type" => "events", + "attributes" => { + "subjId" => "https://doi.org/10.18713/jimis-170117-1-2", + "subj" => { + "@id": "https://doi.org/10.18713/jimis-170117-1-2", + "@type": "ScholarlyArticle", + "datePublished": "2017", + "proxyIdentifiers": [], + "registrantId": "datacite.inist.umr7300", + }, + "obj" => { + "@id": "https://doi.org/10.1016/j.jastp.2013.05.001", + "@type": "ScholarlyArticle", + "datePublished": "2013-09", + "proxyIdentifiers": %w[13646826], + "registrantId": "datacite.crossref.citations", + }, + "objId" => "https://doi.org/10.1016/j.jastp.2013.05.001", + "relationTypeId" => "references", + "sourceId" => "datacite-crossref", + "sourceToken" => "sourceToken", + }, + }, + } + end + + it "has registrant aggregation" do + post uri, params, headers + + expect(last_response.status).to eq(201) + expect(json["errors"]).to be_nil + expect(json.dig("data", "id")).not_to eq(event.uuid) + expect(json.dig("data", "attributes", "objId")).to eq( + "https://doi.org/10.1016/j.jastp.2013.05.001", + ) + + Event.import + sleep 2 + get uri, nil, headers + + expect(json.dig("meta", "registrants", 0, "count")).to eq(1) + expect(json.dig("meta", "registrants", 0, "id")).to eq( + "datacite.crossref.citations", + ) + end + end + + context "with nested attributes" do + let(:uri) { "/v3/events" } + let(:params) do + { + "data" => { + "type" => "events", + "attributes" => { + "subjId" => "https://doi.org/10.18713/jimis-170117-1-2", + "subj" => { + "@id": "https://doi.org/10.18713/jimis-170117-1-2", + "@type": "ScholarlyArticle", + "datePublished": "2017", + "proxyIdentifiers": [], + "registrantId": "datacite.inist.umr7300", + }, + "obj" => { + "@id": "https://doi.org/10.1016/j.jastp.2013.05.001", + "@type": "ScholarlyArticle", + "datePublished": "2013-09", + "proxyIdentifiers": %w[13646826], + "registrantId": "datacite.crossref.citations", + }, + "objId" => "https://doi.org/10.1016/j.jastp.2013.05.001", + "relationTypeId" => "references", + "sourceId" => "datacite-crossref", + "sourceToken" => "sourceToken", + }, + }, + } + end + + it "are correctly stored" do + post uri, params, headers + + expect(last_response.status).to eq(201) + event = Event.where(uuid: json.dig("data", "id")).first + expect(event[:obj].has_key?("datePublished")).to be_truthy + expect(event[:obj].has_key?("registrantId")).to be_truthy + expect(event[:obj].has_key?("proxyIdentifiers")).to be_truthy + end + end + end + + context "create crossref doi", vcr: true do + let(:uri) { "/v3/events" } + let(:params) do + { + "data" => { + "type" => "events", + "attributes" => { + "subjId" => "https://doi.org/10.7554/elife.01567", + "sourceId" => "crossref-import", + "relationTypeId" => nil, + "sourceToken" => event.source_token, + }, + }, + } + end + + it "registered" do + post uri, params, headers + + expect(last_response.status).to eq(201) + expect(json["errors"]).to be_nil + expect(json.dig("data", "id")).to be_present + expect(json.dig("data", "attributes", "subjId")).to eq( + "https://doi.org/10.7554/elife.01567", + ) + end + end + + context "create crossref doi not found", vcr: true do + let(:uri) { "/v3/events" } + let(:params) do + { + "data" => { + "type" => "events", + "attributes" => { + "subjId" => "https://doi.org/10.3389/fmicb.2019.01425", + "sourceId" => "crossref-import", + "relationTypeId" => nil, + "sourceToken" => event.source_token, + }, + }, + } + end + + it "not registered" do + post uri, params, headers + puts json + expect(last_response.status).to eq(201) + expect(json["errors"]).to be_nil + expect(json.dig("data", "id")).to be_present + expect(json.dig("data", "attributes", "subjId")).to eq( + "https://doi.org/10.3389/fmicb.2019.01425", + ) + end + end + + context "create medra doi", vcr: true do + let(:uri) { "/v3/events" } + let(:params) do + { + "data" => { + "type" => "events", + "attributes" => { + "subjId" => "https://doi.org/10.3280/ecag2018-001005", + "sourceId" => "medra-import", + "relationTypeId" => nil, + "sourceToken" => event.source_token, + }, + }, + } + end + + it "registered" do + post uri, params, headers + + expect(last_response.status).to eq(201) + expect(json["errors"]).to be_nil + expect(json.dig("data", "id")).to be_present + expect(json.dig("data", "attributes", "subjId")).to eq( + "https://doi.org/10.3280/ecag2018-001005", + ) + end + end + + context "create kisti doi", vcr: true do + let(:uri) { "/v3/events" } + let(:params) do + { + "data" => { + "type" => "events", + "attributes" => { + "subjId" => "https://doi.org/10.5012/bkcs.2013.34.10.2889", + "sourceId" => "kisti-import", + "relationTypeId" => nil, + "sourceToken" => event.source_token, + }, + }, + } + end + + it "registered" do + post uri, params, headers + + expect(last_response.status).to eq(201) + expect(json["errors"]).to be_nil + expect(json.dig("data", "id")).to be_present + expect(json.dig("data", "attributes", "subjId")).to eq( + "https://doi.org/10.5012/bkcs.2013.34.10.2889", + ) + end + end + + context "create jalc doi", vcr: true do + let(:uri) { "/v3/events" } + let(:params) do + { + "data" => { + "type" => "events", + "attributes" => { + "subjId" => "https://doi.org/10.1241/johokanri.39.979", + "sourceId" => "jalc-import", + "relationTypeId" => nil, + "sourceToken" => event.source_token, + }, + }, + } + end + + it "registered" do + post uri, params, headers + + expect(last_response.status).to eq(201) + expect(json["errors"]).to be_nil + expect(json.dig("data", "id")).to be_present + expect(json.dig("data", "attributes", "subjId")).to eq( + "https://doi.org/10.1241/johokanri.39.979", + ) + end + end + + context "create op doi", vcr: true do + let(:uri) { "/v3/events" } + let(:params) do + { + "data" => { + "type" => "events", + "attributes" => { + "subjId" => "https://doi.org/10.2903/j.efsa.2018.5239", + "sourceId" => "op-import", + "relationTypeId" => nil, + "sourceToken" => event.source_token, + }, + }, + } + end + + it "registered" do + post uri, params, headers + + expect(last_response.status).to eq(201) + expect(json["errors"]).to be_nil + expect(json.dig("data", "id")).to be_present + expect(json.dig("data", "attributes", "subjId")).to eq( + "https://doi.org/10.2903/j.efsa.2018.5239", + ) + end + end + + context "upsert" do + let(:uri) { "/v3/events/#{event.uuid}" } + let(:params) do + { + "data" => { + "type" => "events", + "id" => event.uuid, + "attributes" => { + "subjId" => event.subj_id, + "subj" => event.subj, + "objId" => event.obj_id, + "relationTypeId" => event.relation_type_id, + "sourceId" => event.source_id, + "sourceToken" => event.source_token, + }, + }, + } + end + + context "as admin user" do + it "JSON" do + put uri, params, headers + + expect(last_response.status).to eq(201) + expect(json["errors"]).to be_nil + expect(json.dig("data", "id")).to eq(event.uuid) + # expect(json.dig("data", "relationships", "dois", "data")).to eq([{"id"=>"10.1371/journal.pmed.0030186", "type"=>"dois"}]) + end + end + + context "as staff user" do + let(:token) { User.generate_token(role_id: "staff_user") } + + it "JSON" do + put uri, params, headers + + expect(last_response.status).to eq(403) + expect(json["errors"]).to eq( + [ + { + "status" => "403", + "title" => "You are not authorized to access this resource.", + }, + ], + ) + expect(json["data"]).to be_nil + end + end + + context "as regular user" do + let(:token) { User.generate_token(role_id: "user") } + + it "JSON" do + put uri, params, headers + + expect(last_response.status).to eq(403) + expect(json["errors"]).to eq( + [ + { + "status" => "403", + "title" => "You are not authorized to access this resource.", + }, + ], + ) + expect(json["data"]).to be_blank + end + end + + context "without sourceToken" do + let(:params) do + { + "data" => { + "type" => "events", + "id" => uuid, + "attributes" => { + "subjId" => event.subj_id, "sourceId" => event.source_id + }, + }, + } + end + + it "JSON" do + put uri, params, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq( + [{ "status" => 422, "title" => "Source token can't be blank" }], + ) + expect(json["data"]).to be_nil + end + end + + context "without sourceId" do + let(:params) do + { + "data" => { + "type" => "events", + "id" => uuid, + "attributes" => { + "subjId" => event.subj_id, "sourceToken" => event.source_token + }, + }, + } + end + + it "JSON" do + put uri, params, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq( + [{ "status" => 422, "title" => "Source can't be blank" }], + ) + expect(json["data"]).to be_blank + end + end + + context "without subjId" do + let(:params) do + { + "data" => { + "type" => "events", + "id" => uuid, + "attributes" => { + "sourceId" => event.source_id, "sourceToken" => event.source_token + }, + }, + } + end + + it "JSON" do + put uri, params, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq( + [{ "status" => 422, "title" => "Subj can't be blank" }], + ) + expect(json["data"]).to be_blank + end + end + + context "with wrong API token" do + let(:headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json; version=2", + "HTTP_AUTHORIZATION" => "Bearer 12345678", + } + end + + it "JSON" do + put uri, params, headers + + expect(last_response.status).to eq(401) + expect(json["errors"]).to eq(errors) + expect(json["data"]).to be_blank + end + end + + context "with missing data param" do + let(:params) do + { + "event" => { + "type" => "events", + "id" => uuid, + "attributes" => { "sourceToken" => "123" }, + }, + } + end + + it "JSON" do + put uri, params, headers + + expect(last_response.status).to eq(422) + expect(json.dig("errors", 0, "title")).to start_with("Invalid payload") + expect(json["data"]).to be_blank + end + end + + context "with params in wrong format" do + let(:params) do + { + "data" => + "10.1371/journal.pone.0036790 2012-05-15 New Dromaeosaurids (Dinosauria: Theropoda) from the Lower Cretaceous of Utah, and the Evolution of the Dromaeosaurid Tail", + } + end + + it "JSON" do + put uri, params, headers + + expect(last_response.status).to eq(422) + error = json["errors"].first + expect(error["status"]).to eq("422") + expect(error["title"]).to start_with("Invalid payload") + expect(json["data"]).to be_blank + end + end + + context "entry already exists" do + let!(:event) { create(:event) } + + it "JSON" do + put uri, params, headers + + expect(last_response.status).to eq(200) + expect(json["errors"]).to be_nil + # expect(json.dig("data", "relationships", "dois", "data")).to eq([{"id"=>"10.1371/journal.pmed.0030186", "type"=>"dois"}]) + end + end + end + + context "update" do + let(:event) { create(:event) } + # let!(:doi) { create(:doi, doi: "10.1371/journal.pmed.0030186", aasm_state: "findable") } + let(:uri) { "/v3/events/#{event.uuid}?include=dois" } + + let(:params) do + { + "data" => { + "type" => "events", + "id" => event.uuid, + "attributes" => { + "subjId" => event.subj_id, + "subj" => event.subj, + "objId" => event.obj_id, + "relationTypeId" => event.relation_type_id, + "sourceId" => event.source_id, + "sourceToken" => event.source_token, + }, + }, + } + end + + context "as admin user" do + it "JSON" do + put uri, params, headers + + expect(last_response.status).to eq(200) + expect(json["errors"]).to be_nil + # expect(json.dig("data", "relationships", "dois", "data")).to eq([{"id"=>"10.1371/journal.pmed.0030186", "type"=>"dois"}]) + end + end + + context "as staff user" do + let(:token) { User.generate_token(role_id: "staff_user") } + + it "JSON" do + put uri, params, headers + + expect(last_response.status).to eq(403) + expect(json["errors"]).to eq( + [ + { + "status" => "403", + "title" => "You are not authorized to access this resource.", + }, + ], + ) + expect(json["data"]).to be_nil + end + end + + context "as regular user" do + let(:token) { User.generate_token(role_id: "user") } + + it "JSON" do + put uri, params, headers + + expect(last_response.status).to eq(403) + expect(json["errors"]).to eq( + [ + { + "status" => "403", + "title" => "You are not authorized to access this resource.", + }, + ], + ) + expect(json["data"]).to be_blank + end + end + + context "with wrong API token" do + let(:headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json; version=2", + "HTTP_AUTHORIZATION" => "Bearer 12345678", + } + end + + it "JSON" do + put uri, params, headers + + expect(last_response.status).to eq(401) + expect(json["errors"]).to eq(errors) + expect(json["data"]).to be_blank + end + end + + context "with missing data param" do + let(:params) do + { + "event" => { + "type" => "events", + "id" => uuid, + "attributes" => { "sourceToken" => "123" }, + }, + } + end + + it "JSON" do + put uri, params, headers + + expect(last_response.status).to eq(422) + expect(json.dig("errors", 0, "title")).to start_with("Invalid payload") + expect(json["data"]).to be_blank + end + end + + context "with params in wrong format" do + let(:params) do + { + "data" => + "10.1371/journal.pone.0036790 2012-05-15 New Dromaeosaurids (Dinosauria: Theropoda) from the Lower Cretaceous of Utah, and the Evolution of the Dromaeosaurid Tail", + } + end + + it "JSON" do + put uri, params, headers + + expect(last_response.status).to eq(422) + error = json["errors"].first + expect(error["status"]).to eq("422") + expect(error["title"]).to start_with("Invalid payload") + expect(json["data"]).to be_blank + end + end + end + + context "show" do + let(:doi) { create(:doi, client: client, aasm_state: "findable") } + let(:source_doi) { create(:doi, client: client, aasm_state: "findable") } + let!(:event) do + create( + :event_for_datacite_crossref, + subj_id: "https://doi.org/#{doi.doi}", + obj_id: "https://doi.org/#{source_doi.doi}", + relation_type_id: "is-referenced-by", + ) + end + + let(:uri) { "/v3/events/#{event.uuid}?include=doi-for-source,doi-for-target" } + + before do + DataciteDoi.import + Event.import + sleep 2 + end + + context "as admin user" do + it "JSON" do + get uri, nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "relationTypeId")).to eq( + "is-referenced-by", + ) + expect(json.dig("data", "attributes", "sourceDoi")).to eq( + source_doi.doi.downcase, + ) + expect(json.dig("data", "attributes", "targetDoi")).to eq( + doi.doi.downcase, + ) + expect(json.dig("data", "attributes", "sourceRelationTypeId")).to eq( + "references", + ) + expect(json.dig("data", "attributes", "targetRelationTypeId")).to eq( + "citations", + ) + end + end + + context "as regular user" do + let(:token) { User.generate_token(role_id: "user") } + + it "JSON" do + get uri, nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "relationTypeId")).to eq( + "is-referenced-by", + ) + expect(json.dig("data", "attributes", "sourceDoi")).to eq( + source_doi.doi.downcase, + ) + expect(json.dig("data", "attributes", "targetDoi")).to eq( + doi.doi.downcase, + ) + expect(json.dig("data", "attributes", "sourceRelationTypeId")).to eq( + "references", + ) + expect(json.dig("data", "attributes", "targetRelationTypeId")).to eq( + "citations", + ) + end + end + + context "as regular user with include subj" do + let(:token) { User.generate_token(role_id: "user") } + + it "JSON" do + get uri, nil, headers + puts last_response.body + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "relationTypeId")).to eq( + "is-referenced-by", + ) + expect(json.dig("data", "attributes", "sourceDoi")).to eq( + source_doi.doi.downcase, + ) + expect(json.dig("data", "attributes", "targetDoi")).to eq( + doi.doi.downcase, + ) + expect(json.dig("data", "attributes", "sourceRelationTypeId")).to eq( + "references", + ) + expect(json.dig("data", "attributes", "targetRelationTypeId")).to eq( + "citations", + ) + end + end + + context "event not found" do + let(:uri) { "/v3/events/#{event.uuid}x" } + + it "JSON" do + get uri, nil, headers + + expect(last_response.status).to eq(404) + expect(json["errors"]).to eq( + [ + { + "status" => "404", + "title" => "The resource you are looking for doesn't exist.", + }, + ], + ) + expect(json["data"]).to be_nil + end + end + end + + context "index" do + # # let!(:event) { create(:event) } + # # let(:uri) { "/v3/events" } + + context "query by source-id by Crawler" do + let(:uri) { "/v3/events?query=datacite" } + + # Exclude the token header. + let(:headers) do + { + "HTTP_ACCEPT" => "application/json", + "HTTP_USER_AGENT" => + "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", + } + end + + it "json" do + get uri, nil, headers + expect(last_response.status).to eq(404) + end + end + end + + context "destroy" do + let(:event) { create(:event) } + let(:uri) { "/v3/events/#{event.uuid}" } + + context "as admin user" do + it "JSON" do + delete uri, nil, headers + + expect(last_response.status).to eq(204) + expect(last_response.body).to be_blank + end + end + + # context "as staff user" do + # let(:token) { User.generate_token(role_id: "staff_user") } + + # it "JSON" do + # delete uri, nil, headers + # expect(last_response.status).to eq(401) + + # response = JSON.parse(last_response.body) + # expect(response["errors"]).to eq(errors) + # expect(response["data"]).to be_nil + # end + # end + + # context "as regular user" do + # let(:token) { User.generate_token(role_id: "user") } + + # it "JSON" do + # delete uri, nil, headers + # expect(last_response.status).to eq(401) + + # response = JSON.parse(last_response.body) + # expect(response["errors"]).to eq(errors) + # expect(response["data"]).to be_nil + # end + # end + + # context "with wrong API key" do + # let(:headers) do + # { "HTTP_ACCEPT" => "application/json", + # "HTTP_AUTHORIZATION" => "Token token=12345678" } + # end + + # it "JSON" do + # delete uri, nil, headers + # expect(last_response.status).to eq(401) + + # response = JSON.parse(last_response.body) + # expect(response["errors"]).to eq(errors) + # expect(response["data"]).to be_nil + # end + # end + + context "event not found" do + let(:uri) { "/v3/events/#{event.uuid}x" } + + it "JSON" do + delete uri, nil, headers + + expect(last_response.status).to eq(404) + expect(json["errors"]).to eq( + [ + { + "status" => "404", + "title" => "The resource you are looking for doesn't exist.", + }, + ], + ) + expect(json["data"]).to be_nil + end + end + end +end diff --git a/spec/requests/v3/exports_spec.rb b/spec/requests/v3/exports_spec.rb new file mode 100644 index 000000000..7c8adbbd8 --- /dev/null +++ b/spec/requests/v3/exports_spec.rb @@ -0,0 +1,175 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe ExportsController, type: :request do + let(:admin_bearer) { User.generate_token } + let(:admin_headers) do + { + "HTTP_ACCEPT" => "text/csv", + "HTTP_AUTHORIZATION" => "Bearer " + admin_bearer, + } + end + + let(:consortium) do + create( + :provider, + role_name: "ROLE_CONSORTIUM", + name: "Virtual Library of Virginia", + symbol: "VIVA", + ) + end + let!(:provider) do + create( + :provider, + role_name: "ROLE_CONSORTIUM_ORGANIZATION", + name: "University of Virginia", + symbol: "UVA", + consortium: consortium, + ) + end + let!(:contact) do + create( + :contact, provider: provider, + ) + end + + describe "GET /v3/export/organizations", elasticsearch: true do + before do + Provider.import + sleep 2 + end + + it "returns organizations", vcr: false do + get "/v3/export/organizations", nil, admin_headers + + expect(last_response.status).to eq(200) + csv = last_response.body.lines + expect(csv.length).to eq(3) + expect(csv[0]).to start_with( + "Name,fabricaAccountId,Parent Organization,Is Active", + ) + expect(csv[1]).to start_with("Virtual Library of Virginia,VIVA,,true") + expect(csv[2]).to start_with("University of Virginia,UVA,VIVA,true") + end + + it "returns organizations from date", vcr: false do + get "/v3/export/organizations?from-date=#{Date.today}", + nil, admin_headers + + expect(last_response.status).to eq(200) + csv = last_response.body.lines + expect(csv.length).to eq(3) + expect(csv[0]).to start_with( + "Name,fabricaAccountId,Parent Organization,Is Active", + ) + expect(csv[1]).to start_with("Virtual Library of Virginia,VIVA,,true") + expect(csv[2]).to start_with("University of Virginia,UVA,VIVA,true") + end + end + + describe "GET /v3/export/repositories", elasticsearch: true do + let(:client) do + create( + :client, + provider: provider, + symbol: "UVA.LIBRARY", + name: "University of Virginia Library", + ) + end + let!(:dois) { create_list(:doi, 3, client: client, aasm_state: "findable") } + + before do + DataciteDoi.import + Client.import + sleep 2 + end + + it "returns repositories", vcr: false do + get "/v3/export/repositories", nil, admin_headers + + expect(last_response.status).to eq(200) + csv = last_response.body.lines + expect(csv.length).to eq(2) + expect(csv[0]).to start_with( + "Repository Name,Repository ID,Organization,isActive", + ) + expect(csv[1]).to start_with( + "University of Virginia Library,UVA.LIBRARY,UVA,true", + ) + dois_total = csv[1].strip.split(",")[15].to_i + expect(dois_total).to eq(3) + dois_missing = csv[1].strip.split(",")[18].to_i + expect(dois_missing).to eq(0) + end + + it "returns repositories from date", vcr: false do + get "/v3/export/repositories?from-date=#{Date.today}", + nil, admin_headers + + expect(last_response.status).to eq(200) + csv = last_response.body.lines + expect(csv.length).to eq(2) + expect(csv[0]).to start_with( + "Repository Name,Repository ID,Organization,isActive", + ) + expect(csv[1]).to start_with( + "University of Virginia Library,UVA.LIBRARY,UVA,true", + ) + dois_total = csv[1].strip.split(",")[15].to_i + expect(dois_total).to eq(3) + dois_missing = csv[1].strip.split(",")[18].to_i + expect(dois_missing).to eq(0) + end + end + + describe "GET /v3/export/contacts", elasticsearch: true do + before do + Provider.import + Contact.import + sleep 2 + end + + it "returns all contacts", vcr: false do + get "/v3/export/contacts", nil, admin_headers + + expect(last_response.status).to eq(200) + csv = last_response.body.lines + expect(csv.length).to eq(2) + expect(csv[0]).to eq( + "uid,fabricaAccountId,fabricaId,email,firstName,lastName,type,createdAt,modifiedAt,deletedAt,isActive\n", + ) + expect(csv[1]).to start_with( + "#{contact.uid},UVA,UVA-josiah@example.org,josiah@example.org,Josiah,Carberry,voting", + ) + end + end + + describe "GET /v3/export/check-indexed-dois", elasticsearch: true do + let(:client) do + create( + :client, + provider: provider, + symbol: "UVA.LIBRARY", + name: "University of Virginia Library", + ) + end + let!(:dois) { create_list(:doi, 3, client: client, aasm_state: "findable") } + + before do + DataciteDoi.import + Client.import + sleep 2 + end + + it "returns repositories with dois not indexed", vcr: false do + get "/v3/export/check-indexed-dois", + nil, admin_headers + puts last_response.body + expect(last_response.status).to eq(202) + csv = last_response.body.lines + expect(csv.length).to eq(1) + expect(csv[0].strip).to eq("OK") + end + end +end diff --git a/spec/requests/v3/heartbeat_spec.rb b/spec/requests/v3/heartbeat_spec.rb new file mode 100644 index 000000000..63657ccc7 --- /dev/null +++ b/spec/requests/v3/heartbeat_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe "/heartbeat", type: :request do + it "get heartbeat" do + get "/heartbeat" + + expect(last_response.status).to eq(200) + expect(last_response.body).to eq("OK") + end +end diff --git a/spec/requests/v3/index_spec.rb b/spec/requests/v3/index_spec.rb new file mode 100644 index 000000000..e960d0a77 --- /dev/null +++ b/spec/requests/v3/index_spec.rb @@ -0,0 +1,342 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe IndexController, type: :request do + let(:doi) { create(:doi, aasm_state: "findable") } + + describe "content_negotation" do + context "application/vnd.jats+xml" do + it "returns the Doi" do + get "/#{doi.doi}", + nil, + { "HTTP_ACCEPT" => "application/vnd.jats+xml" } + + expect(last_response.status).to eq(200) + jats = + Maremma.from_xml(last_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 + end + + context "application/vnd.jats+xml link" do + it "returns the Doi" do + get "/application/vnd.jats+xml/#{doi.doi}" + + expect(last_response.status).to eq(200) + jats = + Maremma.from_xml(last_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 + end + + context "application/vnd.datacite.datacite+xml" do + it "returns the Doi" do + get "/#{doi.doi}", + nil, + { + "HTTP_ACCEPT" => "application/vnd.datacite.datacite+xml", + } + + expect(last_response.status).to eq(200) + data = Maremma.from_xml(last_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 + end + + context "application/vnd.datacite.datacite+xml link" do + it "returns the Doi" do + get "/application/vnd.datacite.datacite+xml/#{doi.doi}" + + expect(last_response.status).to eq(200) + data = Maremma.from_xml(last_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 + 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, regenerate: false, aasm_state: "findable") } + + # it 'returns the Doi' do + # get "/#{doi.doi}", nil, { "HTTP_ACCEPT" => "application/vnd.datacite.datacite+xml" } + + # expect(last_response.status).to eq(200) + # data = Maremma.from_xml(last_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 + # end + + context "application/vnd.datacite.datacite+xml not found" do + it "returns error message" do + get "/xxx", + nil, + { + "HTTP_ACCEPT" => "application/vnd.datacite.datacite+xml", + } + + expect(last_response.status).to eq(404) + expect(json["errors"]).to eq( + [ + { + "status" => "404", + "title" => "The resource you are looking for doesn't exist.", + }, + ], + ) + end + end + + context "application/vnd.datacite.datacite+json" do + it "returns the Doi" do + get "/#{doi.doi}", + nil, + { + "HTTP_ACCEPT" => "application/vnd.datacite.datacite+json", + } + + expect(last_response.status).to eq(200) + expect(json["doi"]).to eq(doi.doi) + end + end + + context "application/vnd.datacite.datacite+json link" do + it "returns the Doi" do + get "/application/vnd.datacite.datacite+json/#{doi.doi}" + + expect(last_response.status).to eq(200) + expect(json["doi"]).to eq(doi.doi) + end + end + + context "application/vnd.crosscite.crosscite+json" do + it "returns the Doi" do + get "/#{doi.doi}", + nil, + { + "HTTP_ACCEPT" => "application/vnd.crosscite.crosscite+json", + } + + expect(last_response.status).to eq(200) + expect(json["doi"]).to eq(doi.doi) + end + end + + context "application/vnd.crosscite.crosscite+json link" do + it "returns the Doi" do + get "/application/vnd.crosscite.crosscite+json/#{doi.doi}" + + expect(last_response.status).to eq(200) + expect(json["doi"]).to eq(doi.doi) + end + end + + context "application/vnd.schemaorg.ld+json" do + it "returns the Doi" do + get "/#{doi.doi}", + nil, + { "HTTP_ACCEPT" => "application/vnd.schemaorg.ld+json" } + + expect(last_response.status).to eq(200) + expect(json["@type"]).to eq("Dataset") + end + end + + context "application/vnd.schemaorg.ld+json link" do + it "returns the Doi" do + get "/application/vnd.schemaorg.ld+json/#{doi.doi}" + + expect(last_response.status).to eq(200) + expect(json["@type"]).to eq("Dataset") + end + end + + context "application/ld+json" do + it "returns the Doi" do + get "/#{doi.doi}", + nil, { "HTTP_ACCEPT" => "application/ld+json" } + + expect(last_response.status).to eq(200) + expect(json["@type"]).to eq("Dataset") + end + end + + context "application/ld+json link" do + it "returns the Doi" do + get "/application/ld+json/#{doi.doi}" + + expect(last_response.status).to eq(200) + expect(json["@type"]).to eq("Dataset") + end + end + + context "application/vnd.citationstyles.csl+json" do + it "returns the Doi" do + get "/#{doi.doi}", + nil, + { + "HTTP_ACCEPT" => "application/vnd.citationstyles.csl+json", + } + + expect(last_response.status).to eq(200) + expect(json["type"]).to eq("dataset") + end + end + + context "application/vnd.citationstyles.csl+json link" do + it "returns the Doi" do + get "/application/vnd.citationstyles.csl+json/#{doi.doi}" + + expect(last_response.status).to eq(200) + expect(json["type"]).to eq("dataset") + end + end + + context "application/x-research-info-systems" do + it "returns the Doi" do + get "/#{doi.doi}", + nil, + { "HTTP_ACCEPT" => "application/x-research-info-systems" } + + expect(last_response.status).to eq(200) + expect(last_response.body).to start_with("TY - DATA") + end + end + + context "application/x-research-info-systems link" do + it "returns the Doi" do + get "/application/x-research-info-systems/#{doi.doi}" + + expect(last_response.status).to eq(200) + expect(last_response.body).to start_with("TY - DATA") + end + end + + context "application/x-bibtex" do + it "returns the Doi" do + get "/#{doi.doi}", + nil, { "HTTP_ACCEPT" => "application/x-bibtex" } + + expect(last_response.status).to eq(200) + expect(last_response.body).to start_with( + "@misc{https://doi.org/#{doi.doi.downcase}", + ) + end + end + + context "application/x-bibtex link" do + it "returns the Doi" do + get "/application/x-bibtex/#{doi.doi}" + + expect(last_response.status).to eq(200) + expect(last_response.body).to start_with( + "@misc{https://doi.org/#{doi.doi.downcase}", + ) + end + end + + context "text/csv" do + it "returns the Doi" do + get "/#{doi.doi}", nil, { "HTTP_ACCEPT" => "text/csv" } + + expect(last_response.status).to eq(200) + expect(last_response.body).to include(doi.doi) + end + end + + context "text/csv link" do + it "returns the Doi" do + get "/text/csv/#{doi.doi}" + + expect(last_response.status).to eq(200) + expect(last_response.body).to include(doi.doi) + end + end + + context "text/x-bibliography" do + context "default style" do + it "returns the Doi" do + get "/#{doi.doi}", + nil, { "HTTP_ACCEPT" => "text/x-bibliography" } + + expect(last_response.status).to eq(200) + expect(last_response.body).to start_with("Ollomo, B.") + end + end + + it "header with style" do + get "/#{doi.doi}", + nil, + { "HTTP_ACCEPT" => "text/x-bibliography; style=ieee" } + + expect(last_response.status).to eq(200) + expect(last_response.body).to start_with("B. Ollomo") + end + + it "header with style and locale" do + get "/#{doi.doi}", + nil, + { + "HTTP_ACCEPT" => "text/x-bibliography; style=vancouver; locale=de", + } + + expect(last_response.status).to eq(200) + expect(last_response.body).to start_with("Ollomo B") + end + + context "default style link" do + it "returns the Doi" do + get "/text/x-bibliography/#{doi.doi}" + + expect(last_response.status).to eq(200) + expect(last_response.body).to start_with("Ollomo, B.") + end + end + + context "ieee style link" do + it "returns the Doi" do + get "/text/x-bibliography/#{doi.doi}?style=ieee" + + expect(last_response.status).to eq(200) + expect(last_response.body).to start_with("B. Ollomo") + end + end + end + + context "unknown content type" do + it "returns the Doi" do + get "/#{doi.doi}", + nil, + { "HTTP_ACCEPT" => "application/vnd.ms-excel" } + + expect(last_response.status).to eq(303) + expect(last_response.headers["Location"]).to eq(doi.url) + end + end + + context "missing content type" do + it "returns the Doi" do + get "/#{doi.doi}" + + expect(last_response.status).to eq(303) + expect(last_response.headers["Location"]).to eq(doi.url) + end + end + end +end diff --git a/spec/requests/v3/media_spec.rb b/spec/requests/v3/media_spec.rb new file mode 100644 index 000000000..b34563847 --- /dev/null +++ b/spec/requests/v3/media_spec.rb @@ -0,0 +1,283 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe MediaController, + type: :request, order: :defined, elasticsearch: true do + let(:provider) { create(:provider, symbol: "ADMIN") } + let(:client) { create(:client, provider: provider) } + let(:datacite_doi) { create(:doi, client: client, type: "DataciteDoi") } + let!(:medias) { create_list(:media, 5, doi: datacite_doi) } + let!(:media) { create(:media, doi: datacite_doi) } + let(:bearer) do + User.generate_token( + role_id: "client_admin", + provider_id: provider.symbol.downcase, + client_id: client.symbol.downcase, + ) + end + let(:headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => "Bearer " + bearer, + } + end + let(:media_type) { "application/xml" } + let(:url) { "https://example.org" } + + describe "GET /v3/dois/DOI/media" do + it "returns media" do + get "/v3/dois/#{datacite_doi.doi}/media", nil, headers + + expect(last_response.status).to eq(200) + expect(json).not_to be_empty + expect(json["data"].size).to eq(6) + result = json["data"].first + expect(result.dig("attributes", "mediaType")).to eq("application/json") + end + end + + describe "GET /v3/media query by doi not found" do + it "returns media" do + get "/v3/dois/xxx/media", nil, headers + + expect(json).not_to be_empty + expect(json["errors"]).to eq( + [ + { + "status" => "404", + "title" => "The resource you are looking for doesn't exist.", + }, + ], + ) + end + + it "returns status code 404" do + get "/v3/dois/xxx/media", nil, headers + + expect(last_response.status).to eq(404) + end + end + + describe "GET /v3/dois/DOI/media/:id" do + context "when the record exists" do + it "returns the media" do + get "/v3/dois/#{datacite_doi.doi}/media/#{media.uid}", + nil, headers + + expect(json).not_to be_empty + expect(json.dig("data", "id")).to eq(media.uid) + end + + it "returns status code 200" do + get "/v3/dois/#{datacite_doi.doi}/media/#{media.uid}", + nil, headers + + expect(last_response.status).to eq(200) + end + end + + context "when the record does not exist" do + it "returns status code 404" do + get "/v3/dois/#{datacite_doi.doi}/media/xxxx", + nil, headers + + expect(last_response.status).to eq(404) + end + + it "returns a not found message" do + get "/v3/dois/#{datacite_doi.doi}/media/xxxx", + nil, headers + + expect(json["errors"].first).to eq( + "status" => "404", + "title" => "The resource you are looking for doesn't exist.", + ) + end + end + end + + describe "POST /v3/media" do + context "when the request is valid" do + let(:media_type) { "application/xml" } + let(:valid_attributes) do + { + "data" => { + "type" => "media", + "attributes" => { "mediaType" => media_type, "url" => url }, + }, + } + end + + it "creates a media record" do + post "/v3/dois/#{datacite_doi.doi}/media", + valid_attributes, headers + + expect(json.dig("data", "attributes", "mediaType")).to eq(media_type) + expect(json.dig("data", "attributes", "url")).to eq(url) + end + + it "returns status code 201" do + post "/v3/dois/#{datacite_doi.doi}/media", + valid_attributes, headers + + expect(last_response.status).to eq(201) + end + end + + context "when the mediaType is missing" do + let(:valid_attributes) do + { + "data" => { + "type" => "media", + "attributes" => { "mediaType" => nil, "url" => url }, + }, + } + end + + it "returns status code 201" do + post "/v3/dois/#{datacite_doi.doi}/media", + valid_attributes, headers + + expect(last_response.status).to eq(201) + end + + it "creates a media record" do + post "/v3/dois/#{datacite_doi.doi}/media", + valid_attributes, headers + + expect(json.dig("data", "attributes", "url")).to eq(url) + end + end + + context "when the media_type is not valid" do + let(:media_type) { "text" } + let(:valid_attributes) do + { + "data" => { + "type" => "media", + "attributes" => { "mediaType" => media_type, "url" => url }, + "relationships" => { + "doi" => { + "data" => { "type" => "dois", "id" => datacite_doi.doi }, + }, + }, + }, + } + end + + it "returns status code 422" do + post "/v3/dois/#{datacite_doi.doi}/media", + valid_attributes, headers + + expect(last_response.status).to eq(422) + end + + it "returns a validation failure message" do + post "/v3/dois/#{datacite_doi.doi}/media", + valid_attributes, headers + + expect(json["errors"]).to eq( + [{ "source" => "media_type", "title" => "Is invalid" }], + ) + end + end + end + + describe "PATCH /v3/dois/DOI/media/:id" do + context "when the request is valid" do + let(:valid_attributes) do + { + "data" => { + "type" => "media", + "attributes" => { "mediaType" => media_type, "url" => url }, + "relationships" => { + "doi" => { + "data" => { "type" => "dois", "id" => datacite_doi.doi }, + }, + }, + }, + } + end + + it "updates the record" do + patch "/v3/dois/#{datacite_doi.doi}/media/#{media.uid}", + valid_attributes, headers + + expect(json.dig("data", "attributes", "mediaType")).to eq(media_type) + expect(json.dig("data", "attributes", "url")).to eq(url) + expect(json.dig("data", "attributes", "version")).to be > 0 + end + + it "returns status code 200" do + patch "/v3/dois/#{datacite_doi.doi}/media/#{media.uid}", + valid_attributes, headers + + expect(last_response.status).to eq(200) + end + end + + context "when the request is invalid" do + let(:url) { "mailto:info@example.org" } + let(:params) do + { + "data" => { + "type" => "media", + "attributes" => { "mediaType" => media_type, "url" => url }, + "relationships" => { + "doi" => { + "data" => { "type" => "dois", "id" => datacite_doi.doi }, + }, + }, + }, + } + end + + it "returns status code 422" do + patch "/v3/dois/#{datacite_doi.doi}/media/#{media.uid}", + params, headers + + expect(last_response.status).to eq(422) + end + + it "returns a validation failure message" do + patch "/v3/dois/#{datacite_doi.doi}/media/#{media.uid}", + params, headers + + expect(json["errors"].first).to eq( + "source" => "url", "title" => "Is invalid", + ) + end + end + end + + describe "DELETE /v3/dois/DOI/media/:id" do + context "when the resources does exist" do + it "returns status code 204" do + delete "/v3/dois/#{datacite_doi.doi}/media/#{media.uid}", + nil, headers + + expect(last_response.status).to eq(204) + end + end + + context "when the resources doesnt exist" do + it "returns status code 404" do + delete "/v3/dois/#{datacite_doi.doi}/media/xxx", + nil, headers + + expect(last_response.status).to eq(404) + end + + it "returns a validation failure message" do + delete "/v3/dois/#{datacite_doi.doi}/media/xxx", + nil, headers + + expect(json["errors"].first).to eq( + "status" => "404", + "title" => "The resource you are looking for doesn't exist.", + ) + end + end + end +end diff --git a/spec/requests/v3/metadata_spec.rb b/spec/requests/v3/metadata_spec.rb new file mode 100644 index 000000000..24a156ff6 --- /dev/null +++ b/spec/requests/v3/metadata_spec.rb @@ -0,0 +1,230 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe MetadataController, type: :request do + let(:provider) { create(:provider, symbol: "ADMIN") } + let(:client) { create(:client, provider: provider) } + let(:datacite_doi) { create(:doi, client: client, type: "DataciteDoi") } + let(:xml) { file_fixture("datacite.xml").read } + let!(:metadatas) { create_list(:metadata, 5, doi: datacite_doi, xml: xml) } + let!(:metadata) { create(:metadata, doi: datacite_doi, xml: xml) } + let(:bearer) do + User.generate_token( + role_id: "client_admin", + provider_id: provider.symbol.downcase, + client_id: client.symbol.downcase, + ) + end + let(:headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => "Bearer " + bearer, + } + end + + # describe 'GET /dois/DOI/metadata' do + # it 'returns Metadata' do + # get "/dois/#{doi.doi}/metadata", nil, headers + + # expect(json).not_to be_empty + # expect(json['data'].size).to eq(7) + # end + + # it 'returns status code 200' do + # get "/dois/#{doi.doi}/metadata", nil, headers + + # expect(last_response.status).to eq(200) + # end + # end + + describe "GET /v3/dois/DOI/metadata/:id" do + context "when the record exists" do + it "returns the Metadata" do + get "/v3/dois/#{datacite_doi.doi}/metadata/#{metadata.uid}", + nil, headers + + expect(json).not_to be_empty + expect(json.dig("data", "id")).to eq(metadata.uid) + end + + it "returns status code 200" do + get "/v3/dois/#{datacite_doi.doi}/metadata/#{metadata.uid}", + nil, headers + + expect(last_response.status).to eq(200) + end + end + + context "when the record does not exist" do + it "returns status code 404" do + get "/v3/dois/#{datacite_doi.doi}/metadata/xxxx", + nil, headers + + expect(last_response.status).to eq(404) + end + + it "returns a not found message" do + get "/v3/dois/#{datacite_doi.doi}/metadata/xxxx", + nil, headers + + expect(json["errors"]).to eq( + [ + { + "status" => "404", + "title" => "The resource you are looking for doesn't exist.", + }, + ], + ) + end + end + end + + describe "POST /v3/metadata" do + context "when the request is valid" do + let(:valid_attributes) do + { + "data" => { + "type" => "metadata", + "attributes" => { "xml" => Base64.strict_encode64(xml) }, + }, + } + end + + it "creates a metadata record" do + post "/v3/dois/#{datacite_doi.doi}/metadata", + valid_attributes, headers + + expect(Base64.decode64(json.dig("data", "attributes", "xml"))).to eq( + xml, + ) + expect(json.dig("data", "attributes", "namespace")).to eq( + "http://datacite.org/schema/kernel-4", + ) + end + + it "returns status code 201" do + post "/v3/dois/#{datacite_doi.doi}/metadata", + valid_attributes, headers + + expect(last_response.status).to eq(201) + end + end + + context "when the xml is missing" do + let(:not_valid_attributes) { { "data" => { "type" => "metadata" } } } + + it "returns status code 422" do + post "/v3/dois/#{datacite_doi.doi}/metadata", + not_valid_attributes, headers + + expect(last_response.status).to eq(422) + end + + it "returns a validation failure message" do + post "/v3/dois/#{datacite_doi.doi}/metadata", + not_valid_attributes, headers + + expect(json["errors"]).to eq( + [{ "source" => "xml", "title" => "Can't be blank" }], + ) + end + end + + context "when the XML is not valid draft status" do + let(:xml) { file_fixture("datacite_missing_creator.xml").read } + let(:valid_attributes) do + { + "data" => { + "type" => "metadata", + "attributes" => { "xml" => Base64.strict_encode64(xml) }, + "relationships" => { + "doi" => { + "data" => { "type" => "dois", "id" => datacite_doi.doi }, + }, + }, + }, + } + end + + it "returns status code 201" do + post "/v3/dois/#{datacite_doi.doi}/metadata", + valid_attributes, headers + + expect(last_response.status).to eq(201) + end + + # it 'creates a metadata record' do + # expect(Base64.decode64(json.dig('data', 'attributes', 'xml'))).to eq(xml) + # expect(json.dig('data', 'attributes', 'namespace')).to eq("http://datacite.org/schema/kernel-4") + # end + end + + # context 'when the XML is not valid findable status' do + # let(:doi) { create(:doi, client: client, aasm_state: "findable", xml: nil) } + # let(:xml) { "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHJlc291cmNlIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zPSJodHRwOi8vZGF0YWNpdGUub3JnL3NjaGVtYS9rZXJuZWwtNCIgeHNpOnNjaGVtYUxvY2F0aW9uPSJodHRwOi8vZGF0YWNpdGUub3JnL3NjaGVtYS9rZXJuZWwtNCBodHRwOi8vc2NoZW1hLmRhdGFjaXRlLm9yZy9tZXRhL2tlcm5lbC00L21ldGFkYXRhLnhzZCI+CiAgPGlkZW50aWZpZXIgaWRlbnRpZmllclR5cGU9IkRPSSI+MTAuNTQzOC80SzNNLU5ZVkc8L2lkZW50aWZpZXI+CiAgPGNyZWF0b3JzLz4KICA8dGl0bGVzPgogICAgPHRpdGxlPkVhdGluZyB5b3VyIG93biBEb2cgRm9vZDwvdGl0bGU+CiAgPC90aXRsZXM+CiAgPHB1Ymxpc2hlcj5EYXRhQ2l0ZTwvcHVibGlzaGVyPgogIDxwdWJsaWNhdGlvblllYXI+MjAxNjwvcHVibGljYXRpb25ZZWFyPgogIDxyZXNvdXJjZVR5cGUgcmVzb3VyY2VUeXBlR2VuZXJhbD0iVGV4dCI+QmxvZ1Bvc3Rpbmc8L3Jlc291cmNlVHlwZT4KICA8YWx0ZXJuYXRlSWRlbnRpZmllcnM+CiAgICA8YWx0ZXJuYXRlSWRlbnRpZmllciBhbHRlcm5hdGVJZGVudGlmaWVyVHlwZT0iTG9jYWwgYWNjZXNzaW9uIG51bWJlciI+TVMtNDktMzYzMi01MDgzPC9hbHRlcm5hdGVJZGVudGlmaWVyPgogIDwvYWx0ZXJuYXRlSWRlbnRpZmllcnM+CiAgPHN1YmplY3RzPgogICAgPHN1YmplY3Q+ZGF0YWNpdGU8L3N1YmplY3Q+CiAgICA8c3ViamVjdD5kb2k8L3N1YmplY3Q+CiAgICA8c3ViamVjdD5tZXRhZGF0YTwvc3ViamVjdD4KICA8L3N1YmplY3RzPgogIDxkYXRlcz4KICAgIDxkYXRlIGRhdGVUeXBlPSJDcmVhdGVkIj4yMDE2LTEyLTIwPC9kYXRlPgogICAgPGRhdGUgZGF0ZVR5cGU9Iklzc3VlZCI+MjAxNi0xMi0yMDwvZGF0ZT4KICAgIDxkYXRlIGRhdGVUeXBlPSJVcGRhdGVkIj4yMDE2LTEyLTIwPC9kYXRlPgogIDwvZGF0ZXM+CiAgPHJlbGF0ZWRJZGVudGlmaWVycz4KICAgIDxyZWxhdGVkSWRlbnRpZmllciByZWxhdGVkSWRlbnRpZmllclR5cGU9IkRPSSIgcmVsYXRpb25UeXBlPSJSZWZlcmVuY2VzIj4xMC41NDM4LzAwMTI8L3JlbGF0ZWRJZGVudGlmaWVyPgogICAgPHJlbGF0ZWRJZGVudGlmaWVyIHJlbGF0ZWRJZGVudGlmaWVyVHlwZT0iRE9JIiByZWxhdGlvblR5cGU9IlJlZmVyZW5jZXMiPjEwLjU0MzgvNTVFNS1UNUMwPC9yZWxhdGVkSWRlbnRpZmllcj4KICAgIDxyZWxhdGVkSWRlbnRpZmllciByZWxhdGVkSWRlbnRpZmllclR5cGU9IkRPSSIgcmVsYXRpb25UeXBlPSJJc1BhcnRPZiI+MTAuNTQzOC8wMDAwLTAwU1M8L3JlbGF0ZWRJZGVudGlmaWVyPgogIDwvcmVsYXRlZElkZW50aWZpZXJzPgogIDx2ZXJzaW9uPjEuMDwvdmVyc2lvbj4KICA8ZGVzY3JpcHRpb25zPgogICAgPGRlc2NyaXB0aW9uIGRlc2NyaXB0aW9uVHlwZT0iQWJzdHJhY3QiPkVhdGluZyB5b3VyIG93biBkb2cgZm9vZCBpcyBhIHNsYW5nIHRlcm0gdG8gZGVzY3JpYmUgdGhhdCBhbiBvcmdhbml6YXRpb24gc2hvdWxkIGl0c2VsZiB1c2UgdGhlIHByb2R1Y3RzIGFuZCBzZXJ2aWNlcyBpdCBwcm92aWRlcy4gRm9yIERhdGFDaXRlIHRoaXMgbWVhbnMgdGhhdCB3ZSBzaG91bGQgdXNlIERPSXMgd2l0aCBhcHByb3ByaWF0ZSBtZXRhZGF0YSBhbmQgc3RyYXRlZ2llcyBmb3IgbG9uZy10ZXJtIHByZXNlcnZhdGlvbiBmb3IuLi48L2Rlc2NyaXB0aW9uPgogIDwvZGVzY3JpcHRpb25zPgo8L3Jlc291cmNlPgo=" } + # + # let(:valid_attributes) do + # { + # "data" => { + # "type" => "metadata", + # "attributes"=> { + # "xml"=> xml + # }, + # "relationships"=> { + # "doi"=> { + # "data"=> { + # "type"=> "dois", + # "id"=> doi.doi + # } + # } + # } + # } + # } + # end + # before { post '/metadata', valid_attributes.to_json, headers: headers } + # + # it 'returns status code 422' do + # expect(response).to have_http_status(422) + # end + # + # it 'returns a validation failure message' do + # expect(json["errors"]).to eq([{"source"=>"xml", "title"=>"Xml 4:0: ERROR: Element '{http://datacite.org/schema/kernel-4}creators': Missing child element(s). Expected is ( {http://datacite.org/schema/kernel-4}creator )."}]) + # end + # end + end + + describe "DELETE /v3/dois/DOI/metadata/:id" do + context "when the resources does exist" do + it "returns status code 204" do + delete "/v3/dois/#{datacite_doi.doi}/metadata/#{metadata.uid}", + nil, headers + + expect(last_response.status).to eq(204) + end + end + + context "when the resources doesnt exist" do + it "returns status code 404" do + delete "/v3/dois/#{datacite_doi.doi}/metadata/xxx", + nil, headers + + expect(last_response.status).to eq(404) + end + + it "returns a validation failure message" do + delete "/v3/dois/#{datacite_doi.doi}/metadata/xxx", + nil, headers + + expect(json["errors"]).to eq( + [ + { + "status" => "404", + "title" => "The resource you are looking for doesn't exist.", + }, + ], + ) + end + end + end +end diff --git a/spec/requests/v3/prefixes_spec.rb b/spec/requests/v3/prefixes_spec.rb new file mode 100644 index 000000000..a7504aef6 --- /dev/null +++ b/spec/requests/v3/prefixes_spec.rb @@ -0,0 +1,142 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe PrefixesController, type: :request, elasticsearch: true do + let!(:prefixes) { create_list(:prefix, 10) } + let(:bearer) { User.generate_token } + let(:prefix_id) { prefixes.first.uid } + let(:headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => "Bearer " + bearer, + } + end + + describe "GET /v3/prefixes" do + before do + Prefix.import + sleep 2 + end + + it "returns prefixes" do + get "/v3/prefixes", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(10) + end + + it "returns prefixes by id" do + get "/v3/prefixes?id=#{prefixes.first.uid}", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(1) + end + + it "returns prefixes by query" do + get "/v3/prefixes?query=10.508", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(10) + end + end + + describe "GET /v3/prefixes/:id" do + before do + Prefix.import + sleep 2 + end + + context "when the record exists" do + it "returns status code 200" do + get "/v3/prefixes/#{prefix_id}", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "id")).to eq(prefix_id) + end + end + + context "when the prefix does not exist" do + it "returns status code 404" do + get "/v3/prefixes/10.1234", nil, headers + + expect(last_response.status).to eq(404) + expect(json["errors"].first).to eq( + "status" => "404", + "title" => "The resource you are looking for doesn't exist.", + ) + end + end + + context "when the record does not exist" do + it "returns status code 404" do + get "/v3/prefixes/xxx", nil, headers + + expect(last_response.status).to eq(404) + expect(json["errors"].first).to eq( + "status" => "404", + "title" => "The resource you are looking for doesn't exist.", + ) + end + end + end + + describe "PATCH /v3/prefixes/:prefix_id" do + it "returns method not supported error" do + patch "/v3/prefixes/#{prefix_id}", nil, headers + + expect(last_response.status).to eq(405) + expect(json.dig("errors")).to eq( + [{ "status" => "405", "title" => "Method not allowed" }], + ) + end + end + + describe "POST /v3/prefixes" do + before do + Prefix.import + sleep 2 + end + + context "when the request is valid" do + let!(:provider) { create(:provider) } + let(:valid_attributes) do + { "data" => { "type" => "prefixes", "id" => "10.17177" } } + end + + it "returns status code 201" do + post "/v3/prefixes", valid_attributes, headers + + expect(last_response.status).to eq(201) + end + end + + context "when the request is invalid" do + let!(:provider) { create(:provider) } + let(:not_valid_attributes) do + { + "data" => { + "type" => "prefixes", "attributes" => { "uid" => "dsds10.33342" } + }, + } + end + + it "returns status code 422" do + post "/v3/prefixes", not_valid_attributes, headers + + expect(last_response.status).to eq(422) + expect(json["errors"].first).to eq( + "source" => "uid", "title" => "Can't be blank", + ) + end + end + end + + describe "DELETE /v3/prefixes/:id" do + it "returns status code 204" do + delete "/v3/prefixes/#{prefix_id}", nil, headers + + expect(last_response.status).to eq(204) + end + end +end diff --git a/spec/requests/v3/provider_prefixes_spec.rb b/spec/requests/v3/provider_prefixes_spec.rb new file mode 100644 index 000000000..3afd4b8d6 --- /dev/null +++ b/spec/requests/v3/provider_prefixes_spec.rb @@ -0,0 +1,234 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe ProviderPrefixesController, type: :request, elasticsearch: true do + let(:consortium) { create(:provider, role_name: "ROLE_CONSORTIUM") } + let(:provider) do + create( + :provider, + consortium: consortium, + role_name: "ROLE_CONSORTIUM_ORGANIZATION", + password_input: "12345", + ) + end + let(:prefix) { create(:prefix) } + let!(:provider_prefixes) do + create_list(:provider_prefix, 3, provider: provider) + end + let!(:provider_prefixes2) { create_list(:provider_prefix, 2) } + let(:provider_prefix) { create(:provider_prefix) } + let(:bearer) { User.generate_token(role_id: "staff_admin") } + let(:headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => "Bearer " + bearer, + } + end + + before do + ProviderPrefix.import + Prefix.import + Provider.import + sleep 2 + end + + describe "GET /v3/provider-prefixes by consortium" do + it "returns provider-prefixes" do + get "/v3/provider-prefixes?consortium-id=#{consortium.uid}", + nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(3) + expect(json.dig("meta", "years")).to eq( + [{ "count" => 3, "id" => "2021", "title" => "2021" }], + ) + expect(json.dig("meta", "states")).to eq( + [ + { + "count" => 3, + "id" => "without-repository", + "title" => "Without Repository", + }, + ], + ) + expect(json.dig("meta", "providers")).to eq( + [ + { + "count" => 3, + "id" => provider.uid, + "title" => "My provider", + }, + ], + ) + end + end + + describe "GET /v3/provider-prefixes by provider" do + it "returns provider-prefixes" do + get "/v3/provider-prefixes?provider-id=#{provider.symbol.downcase}", + nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(3) + end + end + + describe "GET /v3/provider-prefixes by prefix" do + it "returns provider-prefixes" do + get "/v3/provider-prefixes?prefix-id=#{provider_prefixes.first.prefix_id}", + nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(1) + end + end + + describe "GET /v3/provider-prefixes by provider and prefix" do + it "returns provider-prefixes" do + get "/v3/provider-prefixes?provider-id=#{ + provider.uid + }&prefix-id=#{provider_prefixes.first.prefix_id}", + nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(1) + end + end + + describe "GET /v3/provider-prefixes by partial prefix" do + it "returns provider-prefixes" do + get "/v3/provider-prefixes?query=10.508", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(5) + end + end + + describe "GET /v3/provider-prefixes" do + it "returns provider-prefixes" do + get "/v3/provider-prefixes", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(5) + end + end + + describe "GET /v3/provider-prefixes/:uid" do + context "when the record exists" do + it "returns the provider-prefix" do + get "/v3/provider-prefixes/#{provider_prefix.uid}", + nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "id")).to eq(provider_prefix.uid) + end + end + + context "when the record does not exist" do + it "returns status code 404" do + get "/v3/provider-prefixes/xxx", nil, headers + + expect(last_response.status).to eq(404) + expect(json["errors"].first).to eq( + "status" => "404", + "title" => "The resource you are looking for doesn't exist.", + ) + end + end + end + + describe "PATCH /v3/provider-prefixes/:uid" do + it "returns method not supported error" do + patch "/v3/provider-prefixes/#{provider_prefix.uid}", + nil, headers + + expect(last_response.status).to eq(405) + expect(json.dig("errors")).to eq( + [{ "status" => "405", "title" => "Method not allowed" }], + ) + end + end + + describe "POST /v3/provider-prefixes" do + context "when the request is valid" do + let(:valid_attributes) do + { + "data" => { + "type" => "provider-prefixes", + "relationships": { + "provider": { + "data": { "type": "provider", "id": provider.uid }, + }, + "prefix": { "data": { "type": "prefix", "id": prefix.uid } }, + }, + }, + } + end + + before do + Prefix.import + Provider.import + sleep 2 + end + + it "creates a provider-prefix" do + post "/v3/provider-prefixes", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "id")).not_to be_nil + + sleep 2 + + get "/v3/prefixes?state=unassigned", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("meta", "total")).to eq(0) + + + delete "/v3/provider-prefixes/#{provider_prefix.uid}", nil, headers + + expect(last_response.status).to eq(204) + + sleep 2 + + get "/v3/prefixes?state=unassigned", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("meta", "total")).to eq(1) + end + end + + context "when the request is invalid" do + let!(:provider) { create(:provider) } + let(:not_valid_attributes) do + { "data" => { "type" => "provider-prefixes" } } + end + + it "returns status code 422" do + post "/v3/provider-prefixes", + not_valid_attributes, headers + + expect(last_response.status).to eq(422) + expect(json["errors"].first).to eq( + "source" => "provider", "title" => "Must exist", + ) + end + end + end + + describe "DELETE /v3/provider-prefixes/:uid" do + let!(:provider_prefix) { create(:provider_prefix) } + + before do + ProviderPrefix.import + sleep 2 + end + + it "deletes the prefix" do + delete "/v3/provider-prefixes/#{provider_prefix.uid}", + nil, headers + expect(last_response.status).to eq(204) + end + end +end diff --git a/spec/requests/v3/providers_spec.rb b/spec/requests/v3/providers_spec.rb new file mode 100644 index 000000000..5cc45de56 --- /dev/null +++ b/spec/requests/v3/providers_spec.rb @@ -0,0 +1,1167 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe ProvidersController, type: :request, elasticsearch: true do + let(:consortium) { create(:provider, role_name: "ROLE_CONSORTIUM") } + let(:provider) do + create( + :provider, + consortium: consortium, role_name: "ROLE_CONSORTIUM_ORGANIZATION", + ) + end + let(:token) do + User.generate_token( + role_id: "consortium_admin", provider_id: consortium.symbol.downcase, + ) + end + let(:admin_token) { User.generate_token } + let(:params) do + { + "data" => { + "type" => "providers", + "attributes" => { + "symbol" => "BL", + "name" => "British Library", + "displayName" => "British Library", + "systemEmail" => "bob@example.com", + "website" => "https://www.bl.uk", + "country" => "GB", + }, + }, + } + end + let(:headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => "Bearer " + token, + } + end + let(:admin_headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => "Bearer " + admin_token, + } + end + + describe "GET /v3/providers" do + let!(:providers) { create_list(:provider, 3) } + + before do + Provider.import + sleep 2 + end + + it "returns providers" do + get "/v3/providers", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(3) + expect(json.dig("meta", "total")).to eq(3) + end + end + + describe "GET /v3/providers for consortium" do + let(:consortium) do + create(:provider, symbol: "dc", role_name: "ROLE_CONSORTIUM") + end + let!(:consortium_organization) do + create( + :provider, + consortium: consortium, role_name: "ROLE_CONSORTIUM_ORGANIZATION", + ) + end + let!(:provider) { create(:provider) } + + before do + Provider.import + sleep 2 + end + + it "returns providers" do + get "/v3/providers?consortium-id=dc", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(1) + expect(json.dig("meta", "total")).to eq(1) + end + end + + describe "GET /v3/providers/:id" do + context "when the record exists" do + it "returns the provider" do + get "/v3/providers/#{provider.symbol.downcase}", + nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"]["id"]).to eq(provider.symbol.downcase) + expect(json["meta"]).to eq("repositoryCount" => 0) + end + + it "returns the provider info for member page" do + get "/v3/providers/#{provider.symbol.downcase}", + nil, headers + + expect(json["data"]["attributes"]["twitterHandle"]).to eq( + provider.twitter_handle, + ) + expect(json["data"]["attributes"]["billingInformation"]).to eq( + provider.billing_information, + ) + expect(json["data"]["attributes"]["rorId"]).to eq(provider.ror_id) + end + end + + context "get provider type ROLE_CONTRACTUAL_PROVIDER and check it works " do + let(:provider) do + create( + :provider, + role_name: "ROLE_CONTRACTUAL_PROVIDER", + name: "Contractor", + symbol: "CONTRCTR", + ) + end + + it "get provider" do + get "/v3/providers/#{provider.symbol.downcase}", + nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "id")).to eq(provider.symbol.downcase) + end + end + + context "when the record does not exist" do + it "returns a not found message" do + get "/v3/providers/xxx", nil, headers + + expect(last_response.status).to eq(404) + expect(json["errors"].first).to eq( + "status" => "404", + "title" => "The resource you are looking for doesn't exist.", + ) + end + end + + context "text/csv" do + it "returns status code 200" do + get "/v3/providers/", + nil, + { + "HTTP_ACCEPT" => "text/csv", "Authorization" => "Bearer " + token + } + + expect(last_response.status).to eq(200) + end + end + end + + describe "GET /v3/providers/:id with contacts" do + let!(:contact) { create(:contact, provider: provider, role_name: ["billing"]) } + + before do + Provider.import + Contact.import + sleep 2 + end + + context "when the record exists" do + it "returns the provider" do + get "/v3/providers/#{provider.symbol.downcase}?include=contacts", + nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"]["id"]).to eq(provider.symbol.downcase) + expect(json.dig("data", "relationships", "contacts", "data", 0)).to be_present + expect(json.dig("included", 0, "attributes", "name")).to eq("Josiah Carberry") + expect(json["meta"]).to eq("repositoryCount" => 0) + end + end + end + + describe "GET /v3/providers/:id meta" do + let(:provider) { create(:provider) } + let(:client) { create(:client, provider: provider) } + let!(:dois) { create_list(:doi, 3, client: client, aasm_state: "findable") } + + before do + Provider.import + Client.import + DataciteDoi.import + sleep 2 + end + + it "returns provider" do + get "/v3/providers/#{provider.symbol.downcase}", + nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "id")).to eq(provider.symbol.downcase) + expect(json["meta"]).to eq("repositoryCount" => 1) + end + end + + describe "GET /v3/providers/totals" do + let(:provider) { create(:provider) } + let(:ra) { create(:provider, role_name: "ROLE_REGISTRATION_AGENCY") } + let(:client) { create(:client, provider: provider) } + let!(:prefixes) { create_list(:prefix, 10) } + let!(:dois) { create_list(:doi, 3, client: client, aasm_state: "findable") } + + before do + Provider.import + Client.import + DataciteDoi.import + sleep 2 + end + + it "returns providers" do + get "/v3/providers/totals", nil, headers + + expect(last_response.status).to eq(200) + # expect(json['data'].size).to eq(4) + expect(json.first.dig("count")).to eq(3) + expect(json.first.dig("temporal")).not_to be_nil + end + end + + describe "GET /v3/providers/:id/stats" do + let(:provider) { create(:provider) } + let(:client) { create(:client, provider: provider) } + let!(:dois) do + create_list( + :doi, + 3, + client: client, aasm_state: "findable", type: "DataciteDoi", + ) + end + + before do + Provider.import + Client.import + DataciteDoi.import + sleep 2 + end + + it "returns provider" do + get "/v3/providers/#{provider.symbol.downcase}/stats", + nil, headers + + expect(last_response.status).to eq(200) + expect(json["clients"]).to eq( + [{ "count" => 1, "id" => "2021", "title" => "2021" }], + ) + # expect(json["resourceTypes"]).to eq([{"count"=>3, "id"=>"dataset", "title"=>"Dataset"}]) + expect(json["dois"]).to eq( + [{ "count" => 3, "id" => "2021", "title" => "2021" }], + ) + end + end + + describe "POST /v3/providers" do + context "request is valid" do + let(:logo) do + "data:image/png;base64," + + Base64.strict_encode64(file_fixture("bl.png").read) + end + let(:params) do + { + "data" => { + "type" => "providers", + "attributes" => { + "symbol" => "BL", + "name" => "British Library", + "displayName" => "British Library", + "memberType" => "consortium_organization", + "logo" => logo, + "website" => "https://www.bl.uk", + "salesforceId" => "abc012345678901234", + "region" => "EMEA", + "systemEmail" => "doe@joe.joe", + "country" => "GB", + }, + "relationships": { + "consortium": { + "data": { + "type": "providers", "id": consortium.symbol.downcase + }, + }, + }, + }, + } + end + + it "creates a provider" do + post "/v3/providers", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "name")).to eq("British Library") + expect(json.dig("data", "attributes", "systemEmail")).to eq( + "doe@joe.joe", + ) + expect( + json.dig("data", "relationships", "consortium", "data", "id"), + ).to eq(consortium.symbol.downcase) + end + end + + context "request ability check" do + let!(:providers) { create_list(:provider, 2) } + let(:last_provider_token) do + User.generate_token( + provider_id: providers.last.symbol, role_id: "provider_admin", + ) + end + let(:headers_last) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => "Bearer " + last_provider_token, + } + end + + before do + Provider.import + sleep 2 + end + + it "has no permission" do + get "/v3/providers/#{providers.first.symbol}", + nil, headers_last + + expect(json["data"].dig("attributes", "symbol")).to eq( + providers.first.symbol, + ) + expect(json["data"].dig("attributes", "billingInformation")).to eq(nil) + expect(json["data"].dig("attributes", "twitterHandle")).to eq(nil) + end + end + + context "create provider member_role contractual_member" do + let(:params) do + { + "data" => { + "type" => "providers", + "attributes" => { + "symbol" => "FG", + "name" => "Figshare", + "displayName" => "Figshare", + "region" => "EMEA", + "systemEmail" => "doe@joe.joe", + "website" => "https://www.bl.uk", + "memberType" => "contractual_member", + "country" => "GB", + }, + }, + } + end + + it "creates a provider" do + post "/v3/providers", params, admin_headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "systemEmail")).to eq( + "doe@joe.joe", + ) + expect(json.dig("data", "attributes", "name")).to eq("Figshare") + expect(json.dig("data", "attributes", "memberType")).to eq( + "contractual_member", + ) + end + end + + context "create provider member_role consortium_organization" do + let(:params) do + { + "data" => { + "type" => "providers", + "attributes" => { + "symbol" => "FG", + "name" => "Figshare", + "displayName" => "Figshare", + "region" => "EMEA", + "systemEmail" => "doe@joe.joe", + "website" => "https://www.bl.uk", + "memberType" => "consortium_organization", + "country" => "GB", + }, + "relationships": { + "consortium": { + "data": { + "type": "providers", "id": consortium.symbol.downcase + }, + }, + }, + }, + } + end + + it "creates a provider" do + post "/v3/providers", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "systemEmail")).to eq( + "doe@joe.joe", + ) + expect(json.dig("data", "attributes", "name")).to eq("Figshare") + expect(json.dig("data", "attributes", "memberType")).to eq( + "consortium_organization", + ) + expect( + json.dig("data", "relationships", "consortium", "data", "id"), + ).to eq(consortium.symbol.downcase) + + sleep 1 + + get "/v3/providers/#{ + consortium.symbol.downcase + }?include=consortium-organizations", + nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("included", 0, "attributes", "systemEmail")).to eq( + "doe@joe.joe", + ) + expect(json.dig("included", 0, "attributes", "name")).to eq("Figshare") + expect(json.dig("included", 0, "attributes", "memberType")).to eq( + "consortium_organization", + ) + expect( + json.dig("included", 0, "relationships", "consortium", "data", "id"), + ).to eq(consortium.symbol) + + # get "/providers?consortium-lead-id=#{consortium_lead.symbol.downcase}", nil, headers + + # expect(last_response.status).to eq(200) + + # get "/providers/#{consortium_lead.symbol.downcase}/organizations", nil, headers + + # expect(last_response.status).to eq(200) + end + end + + context "create provider not member_role consortium_organization" do + let(:params) do + { + "data" => { + "type" => "providers", + "attributes" => { + "symbol" => "FG", + "name" => "Figshare", + "displayName" => "Figshare", + "region" => "EMEA", + "systemEmail" => "doe@joe.joe", + "memberType" => "provider", + "website" => "https://www.bl.uk", + "country" => "GB", + }, + "relationships": { + "consortium": { + "data": { + "type": "providers", "id": consortium.symbol.downcase + }, + }, + }, + }, + } + end + + it "creates a provider" do + post "/v3/providers", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "systemEmail")).to be_nil + expect(json.dig("data", "attributes", "name")).to eq("Figshare") + expect(json.dig("data", "attributes", "memberType")).to eq( + "direct_member", + ) + expect( + json.dig("data", "relationships", "consortium", "data", "id"), + ).to be_nil + end + end + + context "create provider not member_role consortium" do + let(:params) do + { + "data" => { + "type" => "providers", + "attributes" => { + "symbol" => "FG", + "name" => "Figshare", + "displayName" => "Figshare", + "region" => "EMEA", + "systemEmail" => "doe@joe.joe", + "website" => "https://www.bl.uk", + "memberType" => "consortium_organization", + "country" => "GB", + }, + "relationships": { + "consortium": { + "data": { "type": "providers", "id": provider.symbol.downcase }, + }, + }, + }, + } + end + + it "creates a provider" do + post "/v3/providers", params, admin_headers + + expect(last_response.status).to eq(422) + expect(json["errors"].first).to eq( + "source" => "consortium_id", + "title" => "The consortium must be of member_type consortium", + "uid" => "fg" + ) + end + end + + context "request is valid with billing information" do + let(:params) do + { + "data" => { + "type" => "providers", + "attributes" => { + "systemEmail" => "jkiritha@andrew.cmu.edu", + "country" => "US", + "created" => "", + "description" => "", + "focusArea" => "general", + "hasPassword" => "[FILTERED]", + "isActive" => true, + "joined" => "", + "keepPassword" => "[FILTERED]", + "logoUrl" => "", + "website" => "https://www.bl.uk", + "name" => "Carnegie Mellon University", + "displayName" => "Carnegie Mellon University", + "organizationType" => "academicInstitution", + "passwordInput" => "[FILTERED]", + "twitterHandle" => "@meekakitty", + "rorId" => "https://ror.org/05njkjr15", + "billingInformation": { + "city" => "barcelona", + "state" => "Rennes", + "country" => "Rennes", + "organization" => "Rennes", + "department" => "Rennes", + "address" => "Rennes", + "postCode" => "122dc", + }, + "region" => "", + "symbol" => "CM", + "updated" => "", + }, + }, + } + end + + it "creates a provider" do + post "/v3/providers", params, admin_headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "systemEmail")).to eq( + "jkiritha@andrew.cmu.edu", + ) + expect( + json.dig("data", "attributes", "billingInformation", "state"), + ).to eq("Rennes") + expect( + json.dig("data", "attributes", "billingInformation", "postCode"), + ).to eq("122dc") + expect(json.dig("data", "attributes", "twitterHandle")).to eq( + "@meekakitty", + ) + expect(json.dig("data", "attributes", "rorId")).to eq( + "https://ror.org/05njkjr15", + ) + end + end + + context "request is valid with contact information" do + let(:params) do + { + "data" => { + "type" => "providers", + "attributes" => { + "systemEmail" => "jkiritha@andrew.cmu.edu", + "country" => "US", + "created" => "", + "description" => "", + "focusArea" => "general", + "hasPassword" => "[FILTERED]", + "isActive" => true, + "joined" => "", + "keepPassword" => "[FILTERED]", + "logoUrl" => "", + "name" => "Carnegie Mellon University", + "displayName" => "Carnegie Mellon University", + "organizationType" => "academicInstitution", + "passwordInput" => "[FILTERED]", + "twitterHandle" => "@eekakitty", + "rorId" => "https://ror.org/05njkjr15", + "technicalContact" => { + "email" => "kristian@example.com", + "givenName" => "Kristian", + "familyName" => "Garza", + }, + "serviceContact" => { + "email" => "martin@example.com", + "givenName" => "Martin", + "familyName" => "Fenner", + }, + "billingContact" => { + "email" => "Trisha@example.com", + "givenName" => "Trisha", + "familyName" => "cruse", + }, + "secondaryBillingContact" => { + "email" => "Trisha@example.com", + "givenName" => "Trisha", + "familyName" => "cruse", + }, + "votingContact" => { + "email" => "robin@example.com", + "givenName" => "Robin", + "familyName" => "Dasler", + }, + "region" => "", + "symbol" => "CM", + "updated" => "", + }, + }, + } + end + + it "creates a provider" do + post "/v3/providers", params, admin_headers + + expect(last_response.status).to eq(200) + expect( + json.dig("data", "attributes", "technicalContact", "email"), + ).to eq("kristian@example.com") + expect( + json.dig("data", "attributes", "technicalContact", "givenName"), + ).to eq("Kristian") + expect( + json.dig("data", "attributes", "technicalContact", "familyName"), + ).to eq("Garza") + expect(json.dig("data", "attributes", "billingContact", "email")).to eq( + "Trisha@example.com", + ) + expect( + json.dig("data", "attributes", "billingContact", "givenName"), + ).to eq("Trisha") + expect( + json.dig("data", "attributes", "billingContact", "familyName"), + ).to eq("cruse") + expect( + json.dig("data", "attributes", "secondaryBillingContact", "email"), + ).to eq("Trisha@example.com") + expect( + json.dig( + "data", + "attributes", + "secondaryBillingContact", + "givenName", + ), + ).to eq("Trisha") + expect( + json.dig( + "data", + "attributes", + "secondaryBillingContact", + "familyName", + ), + ).to eq("cruse") + expect(json.dig("data", "attributes", "serviceContact", "email")).to eq( + "martin@example.com", + ) + expect( + json.dig("data", "attributes", "serviceContact", "givenName"), + ).to eq("Martin") + expect( + json.dig("data", "attributes", "serviceContact", "familyName"), + ).to eq("Fenner") + expect(json.dig("data", "attributes", "votingContact", "email")).to eq( + "robin@example.com", + ) + expect( + json.dig("data", "attributes", "votingContact", "givenName"), + ).to eq("Robin") + expect( + json.dig("data", "attributes", "votingContact", "familyName"), + ).to eq("Dasler") + end + end + + context "request for admin provider with meta" do + let(:params) do + { + "data" => { + "attributes" => { + "meta" => { + "clients" => [ + { "id" => "2019", "title" => "2019", "count" => 1 }, + ], + "dois" => [], + }, + "name" => "Carnegie Mellon University", + "displayName" => "Carnegie Mellon University", + "symbol" => "CMU", + "description" => nil, + "region" => "AMER", + "country" => "US", + "organizationType" => "academicInstitution", + "focusArea" => "general", + "logoUrl" => "", + "systemEmail" => "jkiritha@andrew.cmu.edu", + "isActive" => true, + "passwordInput" => "@change", + "hasPassword" => false, + "keepPassword" => false, + "joined" => "", + }, + "type" => "providers", + }, + } + end + + it "creates a provider" do + post "/v3/providers", params, admin_headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "systemEmail")).to eq( + "jkiritha@andrew.cmu.edu", + ) + end + end + + context "request for admin provider" do + let(:params) do + { + "data" => { + "type" => "providers", + "attributes" => { + "symbol" => "ADMIN", + "name" => "Admin", + "displayName" => "Admin", + "region" => "EMEA", + "systemEmail" => "doe@joe.joe", + "country" => "GB", + }, + }, + } + end + + it "creates a provider" do + post "/v3/providers", params, admin_headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "systemEmail")).to eq( + "doe@joe.joe", + ) + end + end + + context "request uses basic auth" do + let(:params) do + { + "data" => { + "type" => "providers", + "attributes" => { + "symbol" => "BL", + "name" => "British Library", + "displayName" => "British Library", + "website" => "https://www.bl.uk", + "region" => "EMEA", + "systemEmail" => "doe@joe.joe", + "country" => "GB", + }, + }, + } + end + let(:admin) do + create( + :provider, + symbol: "ADMIN", role_name: "ROLE_ADMIN", password_input: "12345", + ) + end + let(:credentials) do + admin.encode_auth_param(username: "ADMIN", password: "12345") + end + let(:headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => "Basic " + credentials, + } + end + + it "creates a provider" do + post "/v3/providers", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "systemEmail")).to eq( + "doe@joe.joe", + ) + end + end + + context "generate random symbol" do + let(:params) do + { + "data" => { + "type" => "providers", + "attributes" => { + "name" => "Admin", + "displayName" => "Admin", + "region" => "EMEA", + "systemEmail" => "doe@joe.joe", + "country" => "GB", + }, + }, + } + end + + it "creates a provider" do + post "/v3/providers", params, admin_headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "symbol")).to match( + /\A[A-Z]{4}\Z/, + ) + end + end + + context "when the request is missing a required attribute" do + let(:params) do + { + "data" => { + "type" => "providers", + "attributes" => { + "symbol" => "BL", + "name" => "British Library", + "displayName" => "British Library", + "website" => "https://www.bl.uk", + "country" => "GB", + }, + }, + } + end + + it "returns a validation failure message" do + post "/v3/providers", params, admin_headers + + expect(last_response.status).to eq(422) + expect(json["errors"].first).to eq( + "source" => "system_email", "title" => "Can't be blank", + "uid" => "bl" + ) + end + end + + context "when the request is missing a data object" do + let(:params) do + { + "type" => "providers", + "attributes" => { + "symbol" => "BL", + "systemEmail" => "timAus", + "name" => "British Library", + "displayName" => "British Library", + "website" => "https://www.bl.uk", + "country" => "GB", + }, + } + end + + it "returns status code 400" do + post "/v3/providers", params, admin_headers + + expect(last_response.status).to eq(400) + end + + # it 'returns a validation failure message' do + # expect(response["exception"]).to eq("#") + # end + end + end + + describe "PUT /v3/providers/:id" do + context "when the record exists" do + let(:params) do + { + "data" => { + "type" => "providers", + "attributes" => { + "name" => "British Library", + "globusUuid" => "9908a164-1e4f-4c17-ae1b-cc318839d6c8", + "displayName" => "British Library", + "memberType" => "consortium_organization", + "website" => "https://www.bl.uk", + "region" => "Americas", + "systemEmail" => "Pepe@mdm.cod", + "country" => "GB", + }, + "relationships": { + "consortium": { + "data": { + "type": "providers", "id": consortium.symbol.downcase + }, + }, + }, + }, + } + end + + it "updates the record" do + put "/v3/providers/#{provider.symbol}", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "displayName")).to eq( + "British Library", + ) + expect(json.dig("data", "attributes", "globusUuid")).to eq( + "9908a164-1e4f-4c17-ae1b-cc318839d6c8", + ) + expect( + json.dig("data", "relationships", "consortium", "data", "id"), + ).to eq(consortium.symbol.downcase) + end + end + + context "when updating as consortium" do + let(:consortium_credentials) do + User.encode_auth_param(username: consortium.symbol, password: "12345") + end + let(:consortium_headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => "Basic " + consortium_credentials, + } + end + let(:params) do + { + "data" => { + "type" => "providers", + "attributes" => { + "name" => "British Library", + "globusUuid" => "9908a164-1e4f-4c17-ae1b-cc318839d6c8", + "displayName" => "British Library", + "memberType" => "consortium_organization", + "website" => "https://www.bl.uk", + "region" => "Americas", + "systemEmail" => "Pepe@mdm.cod", + "country" => "GB", + }, + "relationships": { + "consortium": { + "data": { + "type": "providers", "id": consortium.symbol.downcase + }, + }, + }, + }, + } + end + + it "updates the record" do + put "/v3/providers/#{provider.symbol}", + params, consortium_headers + puts consortium_headers + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "displayName")).to eq( + "British Library", + ) + expect(json.dig("data", "attributes", "globusUuid")).to eq( + "9908a164-1e4f-4c17-ae1b-cc318839d6c8", + ) + expect( + json.dig("data", "relationships", "consortium", "data", "id"), + ).to eq(consortium.symbol.downcase) + end + end + + context "when updating as consortium_organization" do + let(:consortium_organization_credentials) do + User.encode_auth_param(username: provider.symbol, password: "12345") + end + let(:consortium_organization_headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => + "Basic " + consortium_organization_credentials, + } + end + let(:params) do + { + "data" => { + "type" => "providers", + "attributes" => { + "name" => "British Library", + "globusUuid" => "9908a164-1e4f-4c17-ae1b-cc318839d6c8", + "displayName" => "British Library", + "website" => "https://www.bl.uk", + "region" => "Americas", + "systemEmail" => "Pepe@mdm.cod", + "country" => "GB", + }, + }, + } + end + + it "updates the record" do + put "/v3/providers/#{provider.symbol}", + params, consortium_organization_headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "displayName")).to eq( + "British Library", + ) + expect(json.dig("data", "attributes", "globusUuid")).to eq( + "9908a164-1e4f-4c17-ae1b-cc318839d6c8", + ) + end + end + + context "removes globus_uuid" do + let(:params) do + { + "data" => { + "type" => "providers", "attributes" => { "globusUuid" => nil } + }, + } + end + + it "updates the record" do + put "/v3/providers/#{provider.symbol}", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "displayName")).to eq( + "My provider", + ) + expect(json.dig("data", "attributes", "globusUuid")).to be_nil + end + end + + context "invalid globus_uuid" do + let(:params) do + { + "data" => { + "type" => "providers", "attributes" => { "globusUuid" => "abc" } + }, + } + end + + it "updates the record" do + put "/v3/providers/#{provider.symbol}", params, headers + + expect(last_response.status).to eq(422) + expect(json["errors"].first).to eq( + "source" => "globus_uuid", "title" => "Abc is not a valid UUID", "uid" => provider.uid + ) + end + end + + context "ror_id in wrong format" do + let(:params) do + { + "data" => { + "type" => "providers", + "attributes" => { "rorId" => "ror.org/05njkjr15" }, + }, + } + end + + it "raises error" do + put "/v3/providers/#{provider.symbol}", params, headers + + expect(last_response.status).to eq(422) + expect(json["errors"].first).to eq( + "source" => "ror_id", "title" => "ROR ID should be a url", "uid" => provider.uid + ) + end + end + + context "using basic auth" do + let(:params) do + { + "data" => { + "type" => "providers", + "attributes" => { + "name" => "British Library", + "displayName" => "British Library", + "region" => "Americas", + "systemEmail" => "Pepe@mdm.cod", + "website" => "https://www.bl.uk", + "country" => "GB", + }, + }, + } + end + let(:admin) do + create( + :provider, + symbol: "ADMIN", role_name: "ROLE_ADMIN", password_input: "12345", + ) + end + let(:credentials) do + admin.encode_auth_param(username: "ADMIN", password: "12345") + end + let(:headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => "Basic " + credentials, + } + end + + it "updates the record" do + put "/v3/providers/#{provider.symbol}", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "systemEmail")).to eq( + "Pepe@mdm.cod", + ) + end + end + + context "when the resource doesn't exist" do + let(:params) do + { + "data" => { + "type" => "providers", + "attributes" => { + "name" => "British Library", + "displayName" => "British Library", + "region" => "Americas", + "website" => "https://www.bl.uk", + "systemEmail" => "Pepe@mdm.cod", + "country" => "GB", + }, + }, + } + end + + it "returns status code 404" do + put "/v3/providers/xxx", params, headers + + expect(last_response.status).to eq(404) + end + end + end + + describe "DELETE /v3/providers/:id" do + let!(:provider) { create(:provider) } + + before do + Provider.import + sleep 2 + end + + it "deletes the provider" do + delete "/v3/providers/#{provider.symbol.downcase}", + nil, admin_headers + expect(last_response.status).to eq(204) + end + end +end diff --git a/spec/requests/v3/random_spec.rb b/spec/requests/v3/random_spec.rb new file mode 100644 index 000000000..a4a9b86d8 --- /dev/null +++ b/spec/requests/v3/random_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe "random", type: :request do + let(:token) { User.generate_token } + let(:headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => "Bearer " + token, + } + end + + context "random string" do + it "creates a random string" do + get "/v3/random", nil, headers + + expect(last_response.status).to eq(200) + expect(json["phrase"]).to be_present + end + end +end diff --git a/spec/requests/v3/repositories_spec.rb b/spec/requests/v3/repositories_spec.rb new file mode 100644 index 000000000..26fef6ed5 --- /dev/null +++ b/spec/requests/v3/repositories_spec.rb @@ -0,0 +1,630 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe RepositoriesController, type: :request, elasticsearch: true do + let(:ids) { clients.map(&:uid).join(",") } + let(:consortium) { create(:provider, role_name: "ROLE_CONSORTIUM") } + let(:provider) do + create( + :provider, + consortium: consortium, + symbol: "ABC", + role_name: "ROLE_CONSORTIUM_ORGANIZATION", + password_input: "12345", + ) + end + let!(:client) do + create(:client, provider: provider, client_type: "repository") + end + let(:bearer) do + User.generate_token( + role_id: "provider_admin", provider_id: provider.uid, + ) + end + let(:consortium_bearer) do + User.generate_token( + role_id: "consortium_admin", provider_id: consortium.uid, + ) + end + let(:params) do + { + "data" => { + "type" => "clients", + "attributes" => { + "symbol" => provider.symbol + ".IMPERIAL", + "name" => "Imperial College", + "systemEmail" => "bob@example.com", + "salesforceId" => "abc012345678901234", + "clientType" => "repository", + "certificate" => %w[CoreTrustSeal], + }, + "relationships": { + "provider": { + "data": { "type": "providers", "id": provider.uid }, + }, + }, + }, + } + end + let(:headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => "Bearer " + bearer, + } + end + let(:consortium_headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => "Bearer " + consortium_bearer, + } + end + let(:query) { "jamon" } + + describe "GET /v3/repositories", elasticsearch: true do + let!(:clients) { create_list(:client, 3) } + + before do + Client.import + sleep 1 + end + + it "returns repositories" do + get "/v3/repositories", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(4) + expect(json.dig("meta", "total")).to eq(4) + expect(json.dig("meta", "providers").length).to eq(4) + expect(json.dig("meta", "providers").first).to eq( + "count" => 1, + "id" => provider.uid, + "title" => "My provider", + ) + end + end + + # # Test suite for GET /clients + # describe 'GET /clients query' do + # before { get "/clients?query=#{query}", headers: headers } + # + # it 'returns clients' do + # expect(json).not_to be_empty + # expect(json['data'].size).to eq(11) + # end + # + # it 'returns status code 200' do + # expect(response).to have_http_status(200) + # end + # end + + # describe 'GET /clients?ids=', elasticsearch: true do + # before do + # sleep 1 + # get "/clients?ids=#{ids}", headers: headers + # end + + # it 'returns clients' do + # expect(json).not_to be_empty + # expect(json['data'].size).to eq(10) + # end + + # it 'returns status code 200' do + # expect(response).to have_http_status(200) + # end + # end + + describe "GET /v3/repositories/:id" do + context "when the record exists" do + it "returns the repository" do + get "/v3/repositories/#{client.uid}", nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "name")).to eq(client.name) + expect(json.dig("data", "attributes", "globusUuid")).to eq( + "bc7d0274-3472-4a79-b631-e4c7baccc667", + ) + expect(json["meta"]).to eq("doiCount" => 0, "prefixCount" => 0) + end + end + + context "when the record does not exist" do + it "returns status code 404" do + get "/v3/repositories/xxx", nil, headers + + expect(last_response.status).to eq(404) + expect(json["errors"].first).to eq( + "status" => "404", + "title" => "The resource you are looking for doesn't exist.", + ) + end + end + end + + describe "GET /v3/repositories/totals" do + let(:client) { create(:client) } + let!(:datacite_dois) do + create_list( + :doi, + 3, + client: client, aasm_state: "findable", type: "DataciteDoi", + ) + end + + before do + DataciteDoi.import + Client.import + sleep 3 + end + + it "returns repositories" do + get "/v3/repositories/totals", nil, headers + + expect(last_response.status).to eq(200) + expect(json.first.dig("count")).to eq(3) + expect(json.first.dig("states")).to eq( + [{ "count" => 3, "id" => "findable", "title" => "Findable" }], + ) + expect(json.first.dig("temporal")).not_to be_nil + end + end + + describe "GET /v3/repositories/:id meta" do + let(:provider) { create(:provider) } + let(:client) { create(:client) } + let!(:client_prefix) { create(:client_prefix, client: client) } + let!(:datacite_dois) do + create_list( + :doi, + 3, + client: client, aasm_state: "findable", type: "DataciteDoi", + ) + end + + before do + DataciteDoi.import + Provider.import + Client.import + ClientPrefix.import + sleep 3 + end + + it "returns repository" do + get "/v3/repositories/#{client.uid}" + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "name")).to eq(client.name) + expect(json["meta"]).to eq("doiCount" => 3, "prefixCount" => 1) + end + end + + describe "GET /v3/repositories/:id/stats" do + let(:provider) { create(:provider) } + let(:client) { create(:client) } + let!(:datacite_dois) do + create_list( + :doi, + 3, + client: client, aasm_state: "findable", type: "DataciteDoi", + ) + end + + before do + Provider.import + Client.import + DataciteDoi.import + sleep 2 + end + + it "returns repository" do + get "/v3/repositories/#{client.uid}/stats" + + expect(last_response.status).to eq(200) + expect(json["resourceTypes"]).to eq( + [{ "count" => 3, "id" => "dataset", "title" => "Dataset" }], + ) + expect(json["dois"]).to eq( + [{ "count" => 3, "id" => "2021", "title" => "2021" }], + ) + end + end + + describe "POST /v3/repositories" do + context "when the request is valid" do + it "creates a repository" do + post "/v3/repositories", params, headers + + expect(last_response.status).to eq(201) + attributes = json.dig("data", "attributes") + expect(attributes["name"]).to eq("Imperial College") + expect(attributes["systemEmail"]).to eq("bob@example.com") + expect(attributes["certificate"]).to eq(%w[CoreTrustSeal]) + expect(attributes["salesforceId"]).to eq("abc012345678901234") + + relationships = json.dig("data", "relationships") + expect(relationships.dig("provider", "data", "id")).to eq( + provider.uid, + ) + end + end + + context "consortium" do + it "creates a repository" do + post "/v3/repositories", params, consortium_headers + + expect(last_response.status).to eq(201) + attributes = json.dig("data", "attributes") + expect(attributes["name"]).to eq("Imperial College") + expect(attributes["systemEmail"]).to eq("bob@example.com") + expect(attributes["certificate"]).to eq(%w[CoreTrustSeal]) + expect(attributes["salesforceId"]).to eq("abc012345678901234") + + relationships = json.dig("data", "relationships") + expect(relationships.dig("provider", "data", "id")).to eq( + provider.uid, + ) + end + end + + context "when the request is invalid" do + let(:params) do + { + "data" => { + "type" => "repositories", + "attributes" => { + "symbol" => provider.symbol + ".IMPERIAL", + "name" => "Imperial College", + }, + "relationships": { + "provider": { + "data": { "type": "providers", "id": provider.uid }, + }, + }, + }, + } + end + + it "returns status code 422" do + post "/v3/repositories", params, headers + + expect(last_response.status).to eq(422) + expect(json["errors"]).to eq( + [ + { "source" => "system_email", "title" => "Can't be blank", "uid" => "#{provider.uid}.imperial" }, + { "source" => "system_email", "title" => "Is invalid", "uid" => "#{provider.uid}.imperial" }, + ], + ) + end + end + end + + describe "PUT /v3/repositories/:id" do + context "when the record exists" do + let(:params) do + { + "data" => { + "type" => "repositories", + "attributes" => { + "name" => "Imperial College 2", + "clientType" => "periodical", + "globusUuid" => "9908a164-1e4f-4c17-ae1b-cc318839d6c8", + }, + }, + } + end + + it "updates the record" do + put "/v3/repositories/#{client.symbol}", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "name")).to eq( + "Imperial College 2", + ) + expect(json.dig("data", "attributes", "globusUuid")).to eq( + "9908a164-1e4f-4c17-ae1b-cc318839d6c8", + ) + expect(json.dig("data", "attributes", "name")).not_to eq(client.name) + expect(json.dig("data", "attributes", "clientType")).to eq("periodical") + end + end + + context "consortium" do + let(:params) do + { + "data" => { + "type" => "repositories", + "attributes" => { + "name" => "Imperial College 2", + "clientType" => "periodical", + "globusUuid" => "9908a164-1e4f-4c17-ae1b-cc318839d6c8", + }, + }, + } + end + + it "updates the record" do + put "/v3/repositories/#{client.symbol}", + params, consortium_headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "name")).to eq( + "Imperial College 2", + ) + expect(json.dig("data", "attributes", "globusUuid")).to eq( + "9908a164-1e4f-4c17-ae1b-cc318839d6c8", + ) + expect(json.dig("data", "attributes", "name")).not_to eq(client.name) + expect(json.dig("data", "attributes", "clientType")).to eq("periodical") + end + end + + context "removes the globus_uuid" do + let(:params) do + { + "data" => { + "type" => "repositories", "attributes" => { "globusUuid" => nil } + }, + } + end + + it "updates the record" do + put "/v3/repositories/#{client.symbol}", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "name")).to eq("My data center") + expect(json.dig("data", "attributes", "globusUuid")).to be_nil + end + end + + context "transfer repository" do + let(:bearer) { User.generate_token } + let(:staff_headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => "Bearer " + bearer, + } + end + + let(:new_provider) do + create(:provider, symbol: "QUECHUA", password_input: "12345") + end + let!(:prefix) { create(:prefix) } + let!(:provider_prefix) do + create(:provider_prefix, provider: provider, prefix: prefix) + end + let!(:client_prefix) do + create( + :client_prefix, + client: client, + prefix: prefix, + provider_prefix_id: provider_prefix.uid, + ) + end + let(:doi) { create_list(:doi, 10, client: client) } + + let(:params) do + { + "data" => { + "type" => "clients", + "attributes" => { + "mode" => "transfer", "targetId" => new_provider.symbol + }, + }, + } + end + + it "updates the record" do + put "/v3/repositories/#{client.symbol}", + params, staff_headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "name")).to eq("My data center") + expect( + json.dig("data", "relationships", "provider", "data", "id"), + ).to eq("quechua") + expect( + json.dig("data", "relationships", "prefixes", "data").first.dig("id"), + ).to eq(prefix.uid) + + get "/v3/providers/#{provider.symbol}" + + expect( + json.dig("data", "relationships", "prefixes", "data"), + ).to be_empty + + get "/v3/providers/#{new_provider.symbol}" + + expect( + json.dig("data", "relationships", "prefixes", "data").first.dig("id"), + ).to eq(prefix.uid) + + get "/v3/prefixes/#{prefix.uid}" + expect( + json.dig("data", "relationships", "clients", "data").first.dig("id"), + ).to eq(client.uid) + end + end + + context "invalid globus_uuid" do + let(:params) do + { + "data" => { + "type" => "repositories", "attributes" => { "globusUuid" => "abc" } + }, + } + end + + it "updates the record" do + put "/v3/repositories/#{client.symbol}", params, headers + + expect(last_response.status).to eq(422) + expect(json["errors"].first).to eq( + "source" => "globus_uuid", "title" => "Abc is not a valid UUID", + "uid" => client.uid + ) + end + end + + context "using basic auth", vcr: true do + let(:params) do + { + "data" => { + "type" => "repositories", + "attributes" => { "name" => "Imperial College 2" }, + }, + } + end + let(:credentials) do + provider.encode_auth_param( + username: provider.uid, password: "12345", + ) + end + let(:headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => "Basic " + credentials, + } + end + + it "updates the record" do + put "/v3/repositories/#{client.symbol}", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "name")).to eq( + "Imperial College 2", + ) + expect(json.dig("data", "attributes", "name")).not_to eq(client.name) + end + end + + context "updating with ISSNs" do + let(:params) do + { + "data" => { + "type" => "repositories", + "attributes" => { + "name" => "Journal of Insignificant Results", + "clientType" => "periodical", + "issn" => { "electronic" => "1544-9173", "print" => "1545-7885" }, + }, + }, + } + end + + it "updates the record" do + put "/v3/repositories/#{client.symbol}", params, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "attributes", "name")).to eq( + "Journal of Insignificant Results", + ) + expect(json.dig("data", "attributes", "name")).not_to eq(client.name) + expect(json.dig("data", "attributes", "clientType")).to eq("periodical") + expect(json.dig("data", "attributes", "issn")).to eq( + "electronic" => "1544-9173", "print" => "1545-7885", + ) + end + end + + context "when the request is invalid" do + let(:params) do + { + "data" => { + "type" => "repositories", + "attributes" => { + "symbol" => client.symbol + "M", + "email" => "bob@example.com", + "name" => "Imperial College", + }, + }, + } + end + + it "returns status code 422" do + put "/v3/repositories/#{client.symbol}", params, headers + + expect(last_response.status).to eq(422) + expect(json["errors"].first).to eq( + "source" => "symbol", "title" => "Cannot be changed", + "uid" => client.uid + "m" + ) + end + end + end + + describe "DELETE /v3/repositories/:id" do + it "returns status code 204" do + delete "/v3/repositories/#{client.uid}", nil, headers + + expect(last_response.status).to eq(204) + end + + it "returns status code 204 with consortium" do + delete "/v3/repositories/#{client.uid}", + nil, consortium_headers + + expect(last_response.status).to eq(204) + end + + context "when the resource doesnt exist" do + it "returns status code 404" do + delete "/v3/repositories/xxx", nil, headers + + expect(last_response.status).to eq(404) + end + + it "returns a validation failure message" do + delete "/v3/repositories/xxx", nil, headers + + expect(json["errors"].first).to eq( + "status" => "404", + "title" => "The resource you are looking for doesn't exist.", + ) + end + end + end + + describe "doi transfer", elasticsearch: true do + let!(:dois) { create_list(:doi, 3, client: client) } + let(:target) do + create( + :client, + provider: provider, + symbol: provider.symbol + ".TARGET", + name: "Target Client", + ) + end + let(:params) do + { + "data" => { + "type" => "repositories", + "attributes" => { "targetId" => target.symbol }, + }, + } + end + + before do + DataciteDoi.import + sleep 2 + end + + it "transfered all DOIs" do + put "/v3/repositories/#{client.symbol}", params, headers + sleep 1 + + expect(last_response.status).to eq(200) + # expect(Doi.query(nil, client_id: client.symbol.downcase).results.total).to eq(0) + # expect(Doi.query(nil, client_id: target.symbol.downcase).results.total).to eq(3) + end + + it "transfered all DOIs consortium" do + put "/v3/repositories/#{client.symbol}", + params, consortium_headers + sleep 1 + + expect(last_response.status).to eq(200) + # expect(Doi.query(nil, client_id: client.symbol.downcase).results.total).to eq(0) + # expect(Doi.query(nil, client_id: target.symbol.downcase).results.total).to eq(3) + end + end +end diff --git a/spec/requests/v3/repository_prefixes_spec.rb b/spec/requests/v3/repository_prefixes_spec.rb new file mode 100644 index 000000000..f536e157b --- /dev/null +++ b/spec/requests/v3/repository_prefixes_spec.rb @@ -0,0 +1,194 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe RepositoryPrefixesController, type: :request do + let(:prefix) { create(:prefix) } + let(:provider) { create(:provider) } + let(:client) { create(:client, provider: provider) } + let(:provider_prefix) do + create(:provider_prefix, provider: provider, prefix: prefix) + end + let!(:client_prefixes) { create_list(:client_prefix, 5) } + let(:client_prefix) do + create( + :client_prefix, + client: client, prefix: prefix, provider_prefix: provider_prefix, + ) + end + let(:bearer) { User.generate_token(role_id: "staff_admin") } + let(:headers) do + { + "HTTP_ACCEPT" => "application/vnd.api+json", + "HTTP_AUTHORIZATION" => "Bearer " + bearer, + } + end + + describe "GET /v3/repository-prefixes", elasticsearch: true do + before do + Prefix.import + ClientPrefix.import + sleep 2 + end + + it "returns repository-prefixes" do + get "/v3/repository-prefixes", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(5) + end + + it "returns repository-prefixes by repository-id" do + get "/v3/repository-prefixes?repository-id=#{ + client_prefixes.first.client_id + }", + nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(1) + end + + it "returns repository-prefixes by prefix-id" do + get "/v3/repository-prefixes?prefix-id=#{client_prefixes.first.prefix_id}", + nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(1) + end + + it "returns repository-prefixes by partial prefix" do + get "/v3/repository-prefixes?query=10.508", nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(5) + end + + it "returns repository-prefixes by repository-id and prefix-id" do + get "/v3/repository-prefixes?repository-id=#{ + client_prefixes.first.client_id + }&#{client_prefixes.first.prefix_id}", + nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(1) + end + + it "returns prefixes by client-id" do + get "/v3/prefixes?client-id=#{client_prefixes.first.client_id}", + nil, headers + + expect(last_response.status).to eq(200) + expect(json["data"].size).to eq(1) + end + end + + describe "GET /v3/repository-prefixes/:uid" do + context "when the record exists" do + it "returns the repository-prefix" do + get "/v3/repository-prefixes/#{client_prefix.uid}", + nil, headers + + expect(last_response.status).to eq(200) + expect(json.dig("data", "id")).to eq(client_prefix.uid) + end + end + + context "when the record does not exist" do + it "returns status code 404" do + get "/v3/repository-prefixes/xxx", nil, headers + + expect(last_response.status).to eq(404) + expect(json["errors"].first).to eq( + "status" => "404", + "title" => "The resource you are looking for doesn't exist.", + ) + end + end + end + + describe "PATCH /v3/repository-prefixes/:uid" do + it "returns method not supported error" do + patch "/v3/repository-prefixes/#{client_prefix.uid}", + nil, headers + + expect(last_response.status).to eq(405) + expect(json.dig("errors")).to eq( + [{ "status" => "405", "title" => "Method not allowed" }], + ) + end + end + + describe "DELETE /v3/repository-prefixes/:uid", elasticsearch: true do + let!(:client_prefix) do + create( + :client_prefix, + client: client, prefix: prefix, provider_prefix: provider_prefix, + ) + end + + before do + ClientPrefix.import + sleep 2 + end + + it "deletes a repository-prefix" do + delete "/v3/repository-prefixes/#{client_prefix.uid}", + nil, headers + + expect(last_response.status).to eq(204) + end + end + + describe "POST /v3/repository-prefixes", elasticsearch: true do + before do + Prefix.import + Client.import + ProviderPrefix.import + ClientPrefix.import + sleep 3 + end + + context "when the request is valid" do + let(:valid_attributes) do + { + "data" => { + "type" => "client-prefixes", + "relationships": { + "repository": { + "data": { "type": "repository", "id": client.symbol.downcase }, + }, + "provider-prefix": { + "data": { "type": "provider-prefix", "id": provider_prefix.uid }, + }, + "prefix": { "data": { "type": "prefix", "id": prefix.uid } }, + }, + }, + } + end + + it "creates a repository-prefix" do + post "/v3/repository-prefixes", valid_attributes, headers + + expect(last_response.status).to eq(201) + expect(json.dig("data", "id")).not_to be_nil + end + end + + context "when the request is invalid" do + let!(:client) { create(:client) } + let(:not_valid_attributes) do + { "data" => { "type" => "repository-prefixes" } } + end + + it "returns status code 422" do + post "/v3/repository-prefixes", + not_valid_attributes, headers + + expect(last_response.status).to eq(422) + expect(json["errors"].first).to eq( + "source" => "client", "title" => "Must exist", + ) + end + end + end +end From e7027246971bbcf5741029b6d3dfb142c3501c5b Mon Sep 17 00:00:00 2001 From: Martin Fenner Date: Tue, 9 Mar 2021 12:56:14 +0100 Subject: [PATCH 2/3] pull request should trigger tests --- .github/workflows/pull_request.yml | 80 ++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 .github/workflows/pull_request.yml diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml new file mode 100644 index 000000000..d7012598b --- /dev/null +++ b/.github/workflows/pull_request.yml @@ -0,0 +1,80 @@ +name: Pull request +on: + pull_request: + branches: + - master +jobs: + test: + runs-on: ubuntu-latest + services: + memcached: + image: memcached:1.4.31 + ports: + - 11211/udp + mysql: + image: mysql:5.7 + env: + MYSQL_DATABASE: datacite + MYSQL_USER: root + MYSQL_ALLOW_EMPTY_PASSWORD: "yes" + ports: + - 3306 + elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:7.1.1 + env: + discovery.type: single-node + ES_JAVA_OPTS: -Xms512m -Xmx512m + ELASTIC_PASSWORD: changeme + xpack.security.enabled: "false" + http.cors.enabled: "true" + http.cors.allow-origin: "*" + ports: + - 9200 + env: + MEMCACHE_SERVERS: "localhost:11211" + MYSQL_HOST: "127.0.0.1" + MYSQL_DATABASE: datacite + MYSQL_USER: root + ES_HOST: "localhost:9200" + ELASTIC_PASSWORD: "changeme" + SECRET_KEY_BASE: ${{ secrets.SECRET_KEY_BASE }} + SESSION_ENCRYPTED_COOKIE_SALT: ${{ secrets.SESSION_ENCRYPTED_COOKIE_SALT }} + JWT_PRIVATE_KEY: ${{ secrets.JWT_PRIVATE_KEY }} + JWT_PUBLIC_KEY: ${{ secrets.JWT_PUBLIC_KEY }} + MDS_USERNAME: ${{ secrets.MDS_USERNAME }} + MDS_PASSWORD: ${{ secrets.MDS_PASSWORD }} + AWS_REGION: ${{ secrets.AWS_REGION }} + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby 2.6 + uses: actions/setup-ruby@v1 + with: + ruby-version: '2.6.x' + - uses: actions/cache@v2 + with: + path: vendor/bundle + key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} + restore-keys: | + ${{ runner.os }}-gems- + - name: Install + env: + MYSQL_PORT: ${{ job.services.mysql.ports[3306] }} + run: | + cp .env.build .env + gem install bundler + bundle config path vendor/bundle + bundle install --jobs 4 --retry 3 + sudo /etc/init.d/mysql start + bundle exec rails db:setup RAILS_ENV=test + - name: Lint and Test + env: + MYSQL_PORT: ${{ job.services.mysql.ports[3306] }} + ES_HOST: localhost:${{ job.services.elasticsearch.ports[9200] }} + ELASTIC_PASSWORD: "changeme" + MEMCACHE_SERVERS: "localhost:11211" + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + run: | + bundle exec rubocop + bundle exec rspec + echo $? \ No newline at end of file From f8eea89412f67c2cde0c98434f6cc3889059f847 Mon Sep 17 00:00:00 2001 From: Martin Fenner Date: Tue, 9 Mar 2021 15:22:27 +0100 Subject: [PATCH 3/3] fix specs --- spec/requests/exports_spec.rb | 2 +- spec/requests/v3/exports_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/requests/exports_spec.rb b/spec/requests/exports_spec.rb index 27f531c7e..fafdb9b1c 100644 --- a/spec/requests/exports_spec.rb +++ b/spec/requests/exports_spec.rb @@ -140,7 +140,7 @@ "uid,fabricaAccountId,fabricaId,email,firstName,lastName,type,createdAt,modifiedAt,deletedAt,isActive\n", ) expect(csv[1]).to start_with( - "#{contact.uid},UVA,UVA-josiah@example.org,josiah@example.org,Josiah,Carberry,voting", + "#{contact.uid},UVA,UVA-#{contact.email},#{contact.email},Josiah,Carberry,voting", ) end end diff --git a/spec/requests/v3/exports_spec.rb b/spec/requests/v3/exports_spec.rb index 7c8adbbd8..008bf0149 100644 --- a/spec/requests/v3/exports_spec.rb +++ b/spec/requests/v3/exports_spec.rb @@ -140,7 +140,7 @@ "uid,fabricaAccountId,fabricaId,email,firstName,lastName,type,createdAt,modifiedAt,deletedAt,isActive\n", ) expect(csv[1]).to start_with( - "#{contact.uid},UVA,UVA-josiah@example.org,josiah@example.org,Josiah,Carberry,voting", + "#{contact.uid},UVA,UVA-#{contact.email},#{contact.email},Josiah,Carberry,voting", ) end end