ezid-client 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/README.md +18 -15
- data/VERSION +1 -1
- data/lib/ezid/batch_download.rb +131 -0
- data/lib/ezid/client.rb +3 -1
- data/lib/ezid/identifier.rb +5 -10
- data/lib/ezid/metadata.rb +15 -37
- data/lib/ezid/reserved_metadata.rb +26 -0
- data/lib/ezid/status.rb +10 -0
- data/spec/unit/identifier_spec.rb +12 -12
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 141f157b6169fdedba53b58d11e0b9c1b26c7662
|
4
|
+
data.tar.gz: ef0923245729d7f20e079f3581d7647bc7cb5581
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16b22cbb1ba43ab241036710cfb044f66b365dd434a783a31a1370743e063517a530df12b2f72c24c0f3575dabdb8045e49fa673fc01306621d46fe2a8a88683
|
7
|
+
data.tar.gz: f329532cc4d162575b0c3c14a76d8b7ca253963486686e73565ec4f31debd1c498f5804034f2713badac901b0875d7a27eba322a488bd6ef48f073b01246ce70
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -113,21 +113,24 @@ I, [2014-12-04T15:12:48.853964 #86734] INFO -- : EZID DeleteIdentifier -- succe
|
|
113
113
|
|
114
114
|
## Batch Download
|
115
115
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
>>
|
124
|
-
=>
|
125
|
-
>>
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
=> "http://ezid.cdlib.org/download/
|
130
|
-
|
116
|
+
See http://ezid.cdlib.org/doc/apidoc.html#parameters. Repeated values should be given as an array value for the parameter key.
|
117
|
+
|
118
|
+
```
|
119
|
+
>> batch = Ezid::BatchDownload.new(:csv)
|
120
|
+
=> #<Ezid::BatchDownload format=:csv>
|
121
|
+
>> batch.column = ["_id", "_target"]
|
122
|
+
=> ["_id", "_target"]
|
123
|
+
>> batch.createdAfter = Date.today.to_time
|
124
|
+
=> 2016-02-24 00:00:00 -0500
|
125
|
+
>> batch
|
126
|
+
=> #<Ezid::BatchDownload column=["_id", "_target"] createdAfter=1456290000 format=:csv>
|
127
|
+
>> batch.download_url
|
128
|
+
I, [2016-02-24T18:03:40.828005 #1084] INFO -- : EZID BatchDownload -- success: http://ezid.cdlib.org/download/4a63401e17.csv.gz
|
129
|
+
=> "http://ezid.cdlib.org/download/4a63401e17.csv.gz"
|
130
|
+
>> batch.download_file
|
131
|
+
File successfully download to /current/working/directory/4a63401e17.csv.gz.
|
132
|
+
=> nil
|
133
|
+
```
|
131
134
|
|
132
135
|
## Metadata handling
|
133
136
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.3.0
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require "hashie"
|
2
|
+
require "net/http"
|
3
|
+
require "uri"
|
4
|
+
require_relative "reserved_metadata"
|
5
|
+
|
6
|
+
module Ezid
|
7
|
+
class BatchDownloadError < Error; end
|
8
|
+
|
9
|
+
class BatchDownload < Hashie::Dash
|
10
|
+
include Hashie::Extensions::Coercion
|
11
|
+
|
12
|
+
ANVL = "anvl".freeze
|
13
|
+
CSV = "csv".freeze
|
14
|
+
XML = "xml".freeze
|
15
|
+
FORMATS = [ ANVL, CSV, XML ].freeze
|
16
|
+
|
17
|
+
YES = "yes".freeze
|
18
|
+
NO = "no".freeze
|
19
|
+
BOOLEANS = [ YES, NO ].freeze
|
20
|
+
|
21
|
+
TEST = "test".freeze
|
22
|
+
REAL = "real".freeze
|
23
|
+
PERMANENCE = [ TEST, REAL ].freeze
|
24
|
+
|
25
|
+
ARK = "ark".freeze
|
26
|
+
DOI = "doi".freeze
|
27
|
+
URN = "urn".freeze
|
28
|
+
TYPES = [ ARK, DOI, URN, ].freeze
|
29
|
+
|
30
|
+
# CSV Columns
|
31
|
+
ID = "_id".freeze
|
32
|
+
MAPPED_CREATOR = "_mappedCreator".freeze
|
33
|
+
MAPPED_TITLE = "_mappedTitle".freeze
|
34
|
+
MAPPED_PUBLISHER = "_mappedPublisher".freeze
|
35
|
+
MAPPED_DATE = "_mappedDate".freeze
|
36
|
+
MAPPED_TYPE = "_mappedType".freeze
|
37
|
+
|
38
|
+
MAX_DOWNLOAD_TRIES = 300
|
39
|
+
DOWNLOAD_RETRY_INTERVAL = 1
|
40
|
+
|
41
|
+
# Parameters
|
42
|
+
property :format, required: true # {anvl|csv|xml}
|
43
|
+
property :column # repeatable
|
44
|
+
property :notify # repeatable
|
45
|
+
property :convertTimestamps # {yes|no}
|
46
|
+
|
47
|
+
# Search constraints
|
48
|
+
property :createdAfter
|
49
|
+
property :createdBefore
|
50
|
+
property :crossref # {yes|no}
|
51
|
+
property :exported # {yes|no}
|
52
|
+
property :owner # repeatable
|
53
|
+
property :ownergroup # repeatable
|
54
|
+
property :permanence # {test|real}
|
55
|
+
property :profile # (repeatable)
|
56
|
+
property :status # {reserved|public|unavailable} (repeatable)
|
57
|
+
property :type # {ark|doi|urn} (repeatable)
|
58
|
+
property :updatedAfter
|
59
|
+
property :updatedBefore
|
60
|
+
|
61
|
+
coerce_value FalseClass, ->(v) { NO }
|
62
|
+
coerce_value TrueClass, ->(v) { YES }
|
63
|
+
coerce_value DateTime, ->(v) { v.to_time.utc.iso8601 }
|
64
|
+
coerce_value Time, Integer
|
65
|
+
|
66
|
+
def initialize(format, args={})
|
67
|
+
super(args.merge(format: format))
|
68
|
+
end
|
69
|
+
|
70
|
+
def params
|
71
|
+
to_h
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_response
|
75
|
+
@response ||= client.batch_download(params)
|
76
|
+
end
|
77
|
+
|
78
|
+
def reload
|
79
|
+
@response = nil
|
80
|
+
end
|
81
|
+
|
82
|
+
def download_url
|
83
|
+
get_response.download_url
|
84
|
+
end
|
85
|
+
|
86
|
+
def download_file(path: nil)
|
87
|
+
path ||= Dir.getwd
|
88
|
+
fullpath = File.directory?(path) ? File.join(path, download_filename) : path
|
89
|
+
tries = 0
|
90
|
+
begin
|
91
|
+
tries += 1
|
92
|
+
download = Net::HTTP.get_response(download_uri)
|
93
|
+
download.value
|
94
|
+
rescue Net::HTTPServerException => e
|
95
|
+
if download.is_a?(Net::HTTPNotFound)
|
96
|
+
if tries < MAX_DOWNLOAD_TRIES
|
97
|
+
print "Download file not yet available (attempt #{tries} of #{MAX_DOWNLOAD_TRIES})."
|
98
|
+
puts " Trying again in #{DOWNLOAD_RETRY_INTERVAL} second(s) ..."
|
99
|
+
sleep DOWNLOAD_RETRY_INTERVAL
|
100
|
+
retry
|
101
|
+
else
|
102
|
+
raise BatchDownloadError,
|
103
|
+
"Maximum download attempts (#{MAX_DOWNLOAD_TRIES}) reached unsuccessfully."
|
104
|
+
end
|
105
|
+
else
|
106
|
+
raise
|
107
|
+
end
|
108
|
+
else
|
109
|
+
File.open(fullpath, "wb") do |f|
|
110
|
+
f.write(download.body)
|
111
|
+
end
|
112
|
+
puts "File successfully download to #{fullpath}."
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
def download_uri
|
119
|
+
URI(download_url)
|
120
|
+
end
|
121
|
+
|
122
|
+
def download_filename
|
123
|
+
File.basename(download_uri.path)
|
124
|
+
end
|
125
|
+
|
126
|
+
def client
|
127
|
+
Client.new
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
data/lib/ezid/client.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
require "net/http"
|
2
2
|
|
3
|
+
require_relative "error"
|
4
|
+
require_relative "status"
|
3
5
|
require_relative "configuration"
|
4
6
|
require_relative "session"
|
5
7
|
require_relative "metadata"
|
6
8
|
require_relative "identifier"
|
7
9
|
require_relative "proxy_identifier"
|
8
|
-
require_relative "
|
10
|
+
require_relative "batch_download"
|
9
11
|
|
10
12
|
Dir[File.expand_path("../responses/*.rb", __FILE__)].each { |m| require m }
|
11
13
|
Dir[File.expand_path("../requests/*.rb", __FILE__)].each { |m| require m }
|
data/lib/ezid/identifier.rb
CHANGED
@@ -14,11 +14,6 @@ module Ezid
|
|
14
14
|
# Attributes to display on inspect
|
15
15
|
INSPECT_ATTRS = %w( id status target created ).freeze
|
16
16
|
|
17
|
-
# EZID status terms
|
18
|
-
PUBLIC = "public".freeze
|
19
|
-
RESERVED = "reserved".freeze
|
20
|
-
UNAVAILABLE = "unavailable".freeze
|
21
|
-
|
22
17
|
class << self
|
23
18
|
attr_accessor :defaults
|
24
19
|
|
@@ -151,19 +146,19 @@ module Ezid
|
|
151
146
|
# Is the identifier reserved?
|
152
147
|
# @return [Boolean]
|
153
148
|
def reserved?
|
154
|
-
status == RESERVED
|
149
|
+
status == Status::RESERVED
|
155
150
|
end
|
156
151
|
|
157
152
|
# Is the identifier public?
|
158
153
|
# @return [Boolean]
|
159
154
|
def public?
|
160
|
-
status == PUBLIC
|
155
|
+
status == Status::PUBLIC
|
161
156
|
end
|
162
157
|
|
163
158
|
# Is the identifier unavailable?
|
164
159
|
# @return [Boolean]
|
165
160
|
def unavailable?
|
166
|
-
status
|
161
|
+
status.to_s.start_with? Status::UNAVAILABLE
|
167
162
|
end
|
168
163
|
|
169
164
|
# Is the identifier deletable?
|
@@ -182,7 +177,7 @@ module Ezid
|
|
182
177
|
if unavailable? and reason.nil?
|
183
178
|
return
|
184
179
|
end
|
185
|
-
value = UNAVAILABLE
|
180
|
+
value = Status::UNAVAILABLE
|
186
181
|
if reason
|
187
182
|
value += " | #{reason}"
|
188
183
|
end
|
@@ -192,7 +187,7 @@ module Ezid
|
|
192
187
|
# Mark the identifier as public
|
193
188
|
# @return [String] the new status
|
194
189
|
def public!
|
195
|
-
self.status = PUBLIC
|
190
|
+
self.status = Status::PUBLIC
|
196
191
|
end
|
197
192
|
|
198
193
|
protected
|
data/lib/ezid/metadata.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "hashie"
|
2
|
+
require_relative "reserved_metadata"
|
2
3
|
|
3
4
|
module Ezid
|
4
5
|
#
|
@@ -7,6 +8,7 @@ module Ezid
|
|
7
8
|
# @api private
|
8
9
|
#
|
9
10
|
class Metadata < Hashie::Mash
|
11
|
+
include ReservedMetadata
|
10
12
|
|
11
13
|
# EZID metadata field/value separator
|
12
14
|
ANVL_SEPARATOR = ": "
|
@@ -27,16 +29,11 @@ module Ezid
|
|
27
29
|
LINE_CONTINUATION_RE = /\r?\n\s+/
|
28
30
|
# A line ending
|
29
31
|
LINE_ENDING_RE = /\r?\n/
|
30
|
-
#
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
# @note crossref is not included because it is a simple element
|
36
|
-
PROFILES = %w( dc datacite erc ).freeze
|
37
|
-
RESERVED_ALIASES = [ :coowners=, :export=, :profile=, :status=, :target=,
|
38
|
-
:coowners, :export, :profile, :status, :target,
|
39
|
-
:datacenter, :owner, :ownergroup, :shadowedby, :shadows ]
|
32
|
+
# @api private
|
33
|
+
RESERVED_ALIASES = %w(
|
34
|
+
coowners datacenter export owner ownergroup
|
35
|
+
profile shadowedby shadows status target
|
36
|
+
).freeze
|
40
37
|
|
41
38
|
def initialize(data={})
|
42
39
|
super coerce(data)
|
@@ -75,39 +72,20 @@ module Ezid
|
|
75
72
|
|
76
73
|
protected
|
77
74
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
75
|
+
# Overrides Hashie::Mash
|
76
|
+
def convert_key(key)
|
77
|
+
k = super
|
78
|
+
if RESERVED_ALIASES.include?(k)
|
79
|
+
"_#{k}"
|
80
|
+
elsif k =~ /\A(dc|datacite|erc)_/
|
81
|
+
k.sub(/_/, ".")
|
83
82
|
else
|
84
|
-
|
83
|
+
k
|
85
84
|
end
|
86
85
|
end
|
87
86
|
|
88
87
|
private
|
89
88
|
|
90
|
-
def reserved_alias?(name)
|
91
|
-
RESERVED_ALIASES.include?(name)
|
92
|
-
end
|
93
|
-
|
94
|
-
def reserved_alias(name, *args)
|
95
|
-
send("_#{name}", *args)
|
96
|
-
end
|
97
|
-
|
98
|
-
def profile_accessor?(name)
|
99
|
-
PROFILES.include? name.to_s.split("_").first
|
100
|
-
end
|
101
|
-
|
102
|
-
def profile_accessor(name, *args)
|
103
|
-
key = name.to_s.sub("_", ".")
|
104
|
-
if key.end_with?("=")
|
105
|
-
self[key[0..-2]] = args.first
|
106
|
-
else
|
107
|
-
self[key]
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
89
|
def to_time(value)
|
112
90
|
time = value.to_i
|
113
91
|
(time == 0) ? nil : Time.at(time).utc
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Ezid
|
2
|
+
#
|
3
|
+
# EZID reserved metadata elements
|
4
|
+
#
|
5
|
+
# @see http://ezid.cdlib.org/doc/apidoc.html#internal-metadata
|
6
|
+
#
|
7
|
+
module ReservedMetadata
|
8
|
+
COOWNERS = "_coowners".freeze
|
9
|
+
CREATED = "_created".freeze
|
10
|
+
DATACENTER = "_datacenter".freeze
|
11
|
+
EXPORT = "_export".freeze
|
12
|
+
OWNER = "_owner".freeze
|
13
|
+
OWNERGROUP = "_ownergroup".freeze
|
14
|
+
PROFILE = "_profile".freeze
|
15
|
+
SHADOWEDBY = "_shadowedby".freeze
|
16
|
+
SHADOWS = "_shadows".freeze
|
17
|
+
STATUS = "_status".freeze
|
18
|
+
TARGET = "_target".freeze
|
19
|
+
UPDATED = "_updated".freeze
|
20
|
+
|
21
|
+
# Read-only elements
|
22
|
+
READONLY = [
|
23
|
+
CREATED, DATACENTER, OWNER, OWNERGROUP, SHADOWEDBY, SHADOWS, UPDATED
|
24
|
+
].freeze
|
25
|
+
end
|
26
|
+
end
|
data/lib/ezid/status.rb
ADDED
@@ -119,7 +119,7 @@ module Ezid
|
|
119
119
|
|
120
120
|
describe "#delete" do
|
121
121
|
context "when the identifier is reserved" do
|
122
|
-
subject { described_class.new(id: "id", status:
|
122
|
+
subject { described_class.new(id: "id", status: Status::RESERVED) }
|
123
123
|
context "and is persisted" do
|
124
124
|
before { allow(subject).to receive(:persisted?) { true } }
|
125
125
|
it "deletes the identifier" do
|
@@ -136,7 +136,7 @@ module Ezid
|
|
136
136
|
end
|
137
137
|
end
|
138
138
|
context "when identifier is not reserved" do
|
139
|
-
subject { described_class.new(id: "id", status:
|
139
|
+
subject { described_class.new(id: "id", status: Status::PUBLIC) }
|
140
140
|
it "raises an exception" do
|
141
141
|
expect { subject.delete }.to raise_error(Error)
|
142
142
|
end
|
@@ -193,7 +193,7 @@ module Ezid
|
|
193
193
|
it { is_expected.not_to be_unavailable }
|
194
194
|
end
|
195
195
|
context "when the identifier is reserved" do
|
196
|
-
before { subject.status =
|
196
|
+
before { subject.status = Status::RESERVED }
|
197
197
|
it { is_expected.not_to be_public }
|
198
198
|
it { is_expected.to be_reserved }
|
199
199
|
it { is_expected.not_to be_unavailable }
|
@@ -218,7 +218,7 @@ module Ezid
|
|
218
218
|
subject { described_class.new(id: "id", status: status) }
|
219
219
|
describe "#unavailable!" do
|
220
220
|
context "when the status is \"unavailable\"" do
|
221
|
-
let(:status) { "#{
|
221
|
+
let(:status) { "#{Status::UNAVAILABLE} | whatever" }
|
222
222
|
context "and no reason is given" do
|
223
223
|
it "logs a warning" do
|
224
224
|
pending "https://github.com/duke-libraries/ezid-client/issues/46"
|
@@ -238,12 +238,12 @@ module Ezid
|
|
238
238
|
subject.unavailable!("because")
|
239
239
|
end
|
240
240
|
it "should change the status" do
|
241
|
-
expect { subject.unavailable!("because") }.to change(subject, :status).from(status).to("#{
|
241
|
+
expect { subject.unavailable!("because") }.to change(subject, :status).from(status).to("#{Status::UNAVAILABLE} | because")
|
242
242
|
end
|
243
243
|
end
|
244
244
|
end
|
245
245
|
context "when the status is \"reserved\"" do
|
246
|
-
let(:status) {
|
246
|
+
let(:status) { Status::RESERVED }
|
247
247
|
context "and persisted" do
|
248
248
|
before { allow(subject).to receive(:persisted?) { true } }
|
249
249
|
it "raises an exception" do
|
@@ -253,28 +253,28 @@ module Ezid
|
|
253
253
|
context "and not persisted" do
|
254
254
|
before { allow(subject).to receive(:persisted?) { false } }
|
255
255
|
it "changes the status" do
|
256
|
-
expect { subject.unavailable! }.to change(subject, :status).from(
|
256
|
+
expect { subject.unavailable! }.to change(subject, :status).from(Status::RESERVED).to(Status::UNAVAILABLE)
|
257
257
|
end
|
258
258
|
end
|
259
259
|
end
|
260
260
|
context "when the status is \"public\"" do
|
261
|
-
let(:status) {
|
261
|
+
let(:status) { Status::PUBLIC }
|
262
262
|
context "and no reason is given" do
|
263
263
|
it "changes the status" do
|
264
|
-
expect { subject.unavailable! }.to change(subject, :status).from(
|
264
|
+
expect { subject.unavailable! }.to change(subject, :status).from(Status::PUBLIC).to(Status::UNAVAILABLE)
|
265
265
|
end
|
266
266
|
end
|
267
267
|
context "and a reason is given" do
|
268
268
|
it "changes the status and appends the reason" do
|
269
|
-
expect { subject.unavailable!("withdrawn") }.to change(subject, :status).from(
|
269
|
+
expect { subject.unavailable!("withdrawn") }.to change(subject, :status).from(Status::PUBLIC).to("#{Status::UNAVAILABLE} | withdrawn")
|
270
270
|
end
|
271
271
|
end
|
272
272
|
end
|
273
273
|
end
|
274
274
|
describe "#public!" do
|
275
|
-
subject { described_class.new(id: "id", status:
|
275
|
+
subject { described_class.new(id: "id", status: Status::UNAVAILABLE) }
|
276
276
|
it "changes the status" do
|
277
|
-
expect { subject.public! }.to change(subject, :status).from(
|
277
|
+
expect { subject.public! }.to change(subject, :status).from(Status::UNAVAILABLE).to(Status::PUBLIC)
|
278
278
|
end
|
279
279
|
end
|
280
280
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ezid-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Chandek-Stark
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-02-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hashie
|
@@ -83,6 +83,7 @@ files:
|
|
83
83
|
- VERSION
|
84
84
|
- ezid-client.gemspec
|
85
85
|
- lib/ezid-client.rb
|
86
|
+
- lib/ezid/batch_download.rb
|
86
87
|
- lib/ezid/client.rb
|
87
88
|
- lib/ezid/configuration.rb
|
88
89
|
- lib/ezid/error.rb
|
@@ -101,6 +102,7 @@ files:
|
|
101
102
|
- lib/ezid/requests/modify_identifier_request.rb
|
102
103
|
- lib/ezid/requests/request.rb
|
103
104
|
- lib/ezid/requests/server_status_request.rb
|
105
|
+
- lib/ezid/reserved_metadata.rb
|
104
106
|
- lib/ezid/responses/batch_download_response.rb
|
105
107
|
- lib/ezid/responses/create_identifier_response.rb
|
106
108
|
- lib/ezid/responses/delete_identifier_response.rb
|
@@ -114,6 +116,7 @@ files:
|
|
114
116
|
- lib/ezid/responses/response.rb
|
115
117
|
- lib/ezid/responses/server_status_response.rb
|
116
118
|
- lib/ezid/session.rb
|
119
|
+
- lib/ezid/status.rb
|
117
120
|
- lib/ezid/test_helper.rb
|
118
121
|
- spec/integration/client_spec.rb
|
119
122
|
- spec/integration/identifier_spec.rb
|
@@ -143,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
146
|
version: '0'
|
144
147
|
requirements: []
|
145
148
|
rubyforge_project:
|
146
|
-
rubygems_version: 2.4.
|
149
|
+
rubygems_version: 2.4.3
|
147
150
|
signing_key:
|
148
151
|
specification_version: 4
|
149
152
|
summary: Ruby client for EZID API Version 2
|