Skip to content

Commit

Permalink
support random dois in doi creation. #235
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin Fenner committed Jun 16, 2019
1 parent 43b145a commit 7f82fef
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 24 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ end
group :test do
gem 'capybara'
gem 'webmock', '~> 3.1'
gem 'hashdiff', ['>= 1.0.0.beta1', '< 2.0.0']
gem 'vcr', '~> 3.0.3'
gem 'codeclimate-test-reporter', '~> 1.0', '>= 1.0.8'
gem 'factory_bot_rails', '~> 4.8', '>= 4.8.2'
Expand Down
3 changes: 2 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ GEM
tilt
hamster (3.0.0)
concurrent-ruby (~> 1.0)
hashdiff (0.4.0)
hashdiff (1.0.0.beta1)
hashie (3.6.0)
htmlentities (4.3.4)
http-cookie (1.0.3)
Expand Down Expand Up @@ -569,6 +569,7 @@ DEPENDENCIES
graphql-batch (~> 0.4.0)
graphql-cache (~> 0.6.0)!
graphql-errors (~> 0.3.0)
hashdiff (>= 1.0.0.beta1, < 2.0.0)
iso8601 (~> 0.9.0)
json (~> 1.8, >= 1.8.5)
jsonlint (~> 0.2.0)
Expand Down
13 changes: 11 additions & 2 deletions app/controllers/dois_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -386,8 +386,8 @@ def destroy

def random
if params[:prefix].present?
doi = generate_random_doi(params[:prefix], number: params[:number])
render json: { doi: doi }.to_json
dois = generate_random_dois(params[:prefix], number: params[:number], size: params[:size])
render json: { dois: dois }.to_json
else
render json: { errors: [{ status: "422", title: "Parameter prefix is required" }] }.to_json, status: :unprocessable_entity
end
Expand Down Expand Up @@ -584,6 +584,15 @@ def safe_params
p[:relatedIdentifiers], p[:fundingReferences], p[:geoLocations], p[:rightsList],
p[:subjects], p[:contentUrl], p[:schemaVersion]].compact

# generate random DOI if no DOI is provided
if p[:doi].blank? && p[:prefix].present?
p[:doi] = generate_random_dois(p[:prefix]).first
elsif p[:doi].blank? && client_id.present?
client = Client.where('datacentre.symbol = ?', client_id).first
client_prefix = client.client_prefixes.joins(:prefix).first
p[:doi] = generate_random_dois(client_prefix.prefix.prefix).first if client_prefix
end

# replace DOI, but otherwise don't touch the XML
# use Array.wrap(read_attrs.first) as read_attrs may also be [[]]
if meta["from"] == "datacite" && Array.wrap(read_attrs.first).blank?
Expand Down
13 changes: 8 additions & 5 deletions app/models/concerns/helpable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,25 +82,28 @@ def get_url
end
end

def generate_random_doi(str, options={})
def generate_random_dois(str, options={})
prefix = validate_prefix(str)
fail IdentifierError, "No valid prefix found" unless prefix.present?

shoulder = str.split("/", 2)[1].to_s
encode_doi(prefix, shoulder: shoulder, number: options[:number])
encode_doi(prefix, shoulder: shoulder, number: options[:number], size: options[:size])
end

def encode_doi(prefix, options={})
prefix = validate_prefix(prefix)
return nil unless prefix.present?

number = options[:number].to_s.scan(/\d+/).join("").to_i
number = SecureRandom.random_number(UPPER_LIMIT) unless number > 0
shoulder = options[:shoulder].to_s
shoulder += "-" if shoulder.present?
length = 8
split = 4
prefix.to_s + "/" + shoulder + Base32::URL.encode(number, split: split, length: length, checksum: true)
size = (options[:size] || 1).to_i

Array.new(size).map do |a|
n = number > 0 ? number : SecureRandom.random_number(UPPER_LIMIT)
prefix.to_s + "/" + shoulder + Base32::URL.encode(n, split: split, length: length, checksum: true)
end.uniq
end

def epoch_to_utc(epoch)
Expand Down
32 changes: 20 additions & 12 deletions spec/concerns/helpable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,39 +25,47 @@
end
end

context "generate_random_doi" do
context "generate_random_dois" do
it 'should generate' do
str = "10.5072"
expect(subject.generate_random_doi(str).length).to eq(17)
str = "10.14454"
expect(subject.generate_random_dois(str).first.length).to eq(18)
end

it 'should generate multiple' do
str = "10.14454"
size = 10
dois = subject.generate_random_dois(str, size: size)
expect(dois.length).to eq(10)
expect(dois.first).to start_with("10.14454")
end

it 'should generate with seed' do
str = "10.5072"
str = "10.14454"
number = 123456
expect(subject.generate_random_doi(str, number: number)).to eq("10.5072/003r-j076")
expect(subject.generate_random_dois(str, number: number)).to eq(["10.14454/003r-j076"])
end

it 'should generate with seed checksum' do
str = "10.5072"
str = "10.14454"
number = 1234578
expect(subject.generate_random_doi(str, number: number)).to eq("10.5072/015n-mj18")
expect(subject.generate_random_dois(str, number: number)).to eq(["10.14454/015n-mj18"])
end

it 'should generate with another seed checksum' do
str = "10.5072"
str = "10.14454"
number = 1234579
expect(subject.generate_random_doi(str, number: number)).to eq("10.5072/015n-mk15")
expect(subject.generate_random_dois(str, number: number)).to eq(["10.14454/015n-mk15"])
end

it 'should generate with shoulder' do
str = "10.5072/fk2"
str = "10.14454/fk2"
number = 123456
expect(subject.generate_random_doi(str, number: number)).to eq("10.5072/fk2-003r-j076")
expect(subject.generate_random_dois(str, number: number)).to eq(["10.14454/fk2-003r-j076"])
end

it 'should not generate if not DOI prefix' do
str = "20.5438"
expect { subject.generate_random_doi(str) }.to raise_error(IdentifierError, "No valid prefix found")
expect { subject.generate_random_dois(str) }.to raise_error(IdentifierError, "No valid prefix found")
end
end

Expand Down
86 changes: 82 additions & 4 deletions spec/requests/dois_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -823,15 +823,93 @@
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

it 'sets state to findable' do
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 '/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([{"familyName"=>"Fenner",
"givenName"=>"Martin",
"name"=>"Fenner, Martin",
"nameIdentifiers"=>
[{"nameIdentifier"=>"https://orcid.org/0000-0003-1419-2405",
"nameIdentifierScheme"=>"ORCID",
"schemeUri"=>"https://orcid.org"}],
"nameType"=>"Personal"}])
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 random doi with client prefix' 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/patspec.pdf",
"xml" => xml,
"source" => "test",
"event" => "publish"
}
}
}
end

it 'creates a Doi' do
post '/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([{"familyName"=>"Fenner",
"givenName"=>"Martin",
"name"=>"Fenner, Martin",
"nameIdentifiers"=>
[{"nameIdentifier"=>"https://orcid.org/0000-0003-1419-2405",
"nameIdentifierScheme"=>"ORCID",
"schemeUri"=>"https://orcid.org"}],
"nameType"=>"Personal"}])
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

Expand Down Expand Up @@ -2171,7 +2249,7 @@
get '/dois/random?prefix=10.14454', headers: headers

expect(last_response.status).to eq(200)
expect(json['doi']).to start_with("10.14454")
expect(json['dois'].first).to start_with("10.14454")
end
end

Expand Down Expand Up @@ -2263,7 +2341,7 @@
get "/dois/random?prefix=#{prefix.prefix}", nil, headers

expect(last_response.status).to eq(200)
expect(json['doi']).to start_with("10.14454")
expect(json['dois'].first).to start_with("10.14454")
end
end

Expand All @@ -2274,7 +2352,7 @@
get "/dois/random?prefix=10.14454&number=#{number}", nil, headers

expect(last_response.status).to eq(200)
expect(json['doi']).to eq("10.14454/3mfp-6m52")
expect(json['dois'].first).to eq("10.14454/3mfp-6m52")
end
end

Expand Down

0 comments on commit 7f82fef

Please sign in to comment.