ezid-client 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/ezid/request.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require "delegate"
1
2
  require "uri"
2
3
  require "net/http"
3
4
 
@@ -5,50 +6,35 @@ module Ezid
5
6
  #
6
7
  # A request to the EZID service.
7
8
  #
8
- # @note A Request should only be created by an Ezid::Client instance.
9
9
  # @api private
10
- class Request
10
+ class Request < SimpleDelegator
11
11
 
12
- EZID_HOST = "ezid.cdlib.org"
12
+ HOST = "https://ezid.cdlib.org"
13
13
  CHARSET = "UTF-8"
14
14
  CONTENT_TYPE = "text/plain"
15
15
 
16
- attr_reader :http_request, :uri, :operation
17
-
18
- def initialize(*args)
19
- @operation = args
20
- http_method, path, query = Api.send(*args)
21
- @uri = URI::HTTPS.build(host: EZID_HOST, path: path, query: query)
22
- @http_request = Net::HTTP.const_get(http_method).new(uri)
23
- @http_request.set_content_type(CONTENT_TYPE, charset: CHARSET)
16
+ def self.execute(*args)
17
+ request = new(*args)
18
+ yield request if block_given?
19
+ request.execute
24
20
  end
25
21
 
26
- # Executes the request and returns the HTTP response
27
- # @return [Net::HTTPResponse] the response
28
- def execute
29
- Net::HTTP.start(uri.host, use_ssl: true) do |http|
30
- http.request(http_request)
31
- end
22
+ # @param method [Symbol] the Net::HTTP constant for the request method
23
+ # @param path [String] the uri path (including query string, if any)
24
+ def initialize(method, path)
25
+ http_method = Net::HTTP.const_get(method)
26
+ uri = URI.parse([HOST, path].join)
27
+ super(http_method.new(uri))
28
+ set_content_type(CONTENT_TYPE, charset: CHARSET)
32
29
  end
33
30
 
34
- # Adds authentication data to the request
35
- # @param opts [Hash] the options.
36
- # Must include either: `:cookie`, or: `:user` and `:password`.
37
- # @option opts [String] :cookie a session cookie
38
- # @option opts [String] :user user name for basic auth
39
- # @option opts [String] :password password for basic auth
40
- def add_authentication(opts={})
41
- if opts[:cookie]
42
- http_request["Cookie"] = opts[:cookie]
43
- else
44
- http_request.basic_auth(opts[:user], opts[:password])
31
+ # Executes the request and returns the response
32
+ # @return [Ezid::Response] the response
33
+ def execute
34
+ http_response = Net::HTTP.start(uri.host, use_ssl: true) do |http|
35
+ http.request(__getobj__)
45
36
  end
46
- end
47
-
48
- # Adds EZID metadata (if any) to the request body
49
- # @param metadata [Ezid::Metadata] the metadata to add
50
- def add_metadata(metadata)
51
- http_request.body = metadata.to_anvl unless metadata.empty?
37
+ Response.new(http_response)
52
38
  end
53
39
 
54
40
  end
data/lib/ezid/response.rb CHANGED
@@ -4,7 +4,6 @@ module Ezid
4
4
  #
5
5
  # A response from the EZID service.
6
6
  #
7
- # @note A Response should only be created by an Ezid::Client instance.
8
7
  # @api private
9
8
  class Response < SimpleDelegator
10
9
 
@@ -14,6 +13,21 @@ module Ezid
14
13
  # Error response status
15
14
  ERROR = "error"
16
15
 
16
+ IDENTIFIER_RE = /^(doi|ark|urn):[^\s]+/
17
+ SHADOW_ARK_RE = /\| (ark:[^\s]+)/
18
+
19
+ def id
20
+ @id ||= IDENTIFIER_RE.match(message)[0]
21
+ end
22
+
23
+ def shadow_ark
24
+ @shadow_ark ||= SHADOW_ARK_RE.match(message)[1]
25
+ end
26
+
27
+ def metadata
28
+ content[1]
29
+ end
30
+
17
31
  # The response status -- "success" or "error"
18
32
  # @return [String] the status
19
33
  def status
@@ -23,7 +37,7 @@ module Ezid
23
37
  # The status line of the response
24
38
  # @return [String] the status line
25
39
  def status_line
26
- content.first
40
+ content[0]
27
41
  end
28
42
 
29
43
  # The body of the response split into: status line and rest of body
@@ -32,45 +46,45 @@ module Ezid
32
46
  @content ||= body.split(/\r?\n/, 2)
33
47
  end
34
48
 
35
- # Metadata (if any) parsed out of the response
36
- # @return [Ezid::Metadata] the metadata
37
- def metadata
38
- return @metadata if @metadata
39
- if success? && identifier_uri?
40
- @metadata = Metadata.new(content.last)
41
- end
42
- @metadata
43
- end
44
-
45
- # The identifier string parsed out of the response
46
- # @return [String] the identifier
47
- def identifier
48
- message.split(/\s/).first if success? && identifier_uri?
49
- end
50
-
51
- def identifier_uri?
52
- ( uri.path =~ /^\/(id|shoulder)\// ) && true
53
- end
54
-
49
+ # The outcome of the request - "success" or "error"
50
+ # @return [String] the outcome
55
51
  def outcome
56
52
  status.first
57
53
  end
58
54
 
55
+ # The EZID status message
56
+ # @return [String] the message
59
57
  def message
60
58
  status.last
61
59
  end
62
60
 
63
- def cookie
64
- self["Set-Cookie"].split(";").first rescue nil
65
- end
66
-
61
+ # Whether the outcome was an error
62
+ # @return [Boolean]
67
63
  def error?
68
64
  outcome == ERROR
69
65
  end
70
66
 
67
+ # Whether the outcome was a success
68
+ # @return [Boolean]
71
69
  def success?
72
70
  outcome == SUCCESS
73
71
  end
74
72
 
73
+ # Returns an exception instance if there was an error
74
+ # @return [Ezid::Error] the exception
75
+ def exception
76
+ @exception ||= (error? && Error.new(message))
77
+ end
78
+
79
+ # The URI path of the request
80
+ # @return [String] the path
81
+ def uri_path
82
+ __getobj__.uri.path
83
+ end
84
+
85
+ def cookie
86
+ self["Set-Cookie"].split(";").first rescue nil
87
+ end
88
+
75
89
  end
76
90
  end
data/lib/ezid/session.rb CHANGED
@@ -1,8 +1,3 @@
1
- require "uri"
2
- require "net/http"
3
-
4
- require_relative "request"
5
-
6
1
  module Ezid
7
2
  #
8
3
  # An EZID session
@@ -20,9 +15,8 @@ module Ezid
20
15
  super.sub(/@cookie="[^\"]+"/, "OPEN")
21
16
  end
22
17
 
23
- def open(response)
24
- # XXX raise exception if no cookie?
25
- @cookie = response.cookie if response.cookie
18
+ def open(cookie)
19
+ @cookie = cookie
26
20
  end
27
21
 
28
22
  def close
@@ -0,0 +1,23 @@
1
+ module Ezid
2
+ class Status < SimpleDelegator
3
+
4
+ SUBSYSTEMS = %w( noid ldap datacite )
5
+
6
+ SUBSYSTEMS.each do |s|
7
+ define_method(s) { subsystems[s] || "not checked" }
8
+ end
9
+
10
+ def subsystems
11
+ return {} unless content[1]
12
+ content[1].split(/\r?\n/).each_with_object({}) do |line, memo|
13
+ subsystem, status = line.split(": ", 2)
14
+ memo[subsystem] = status
15
+ end
16
+ end
17
+
18
+ def up?
19
+ success?
20
+ end
21
+
22
+ end
23
+ end
@@ -1,90 +1,181 @@
1
1
  module Ezid
2
2
  RSpec.describe Client do
3
+
3
4
  describe "initialization" do
4
5
  describe "without a block" do
5
- it "should not be logged in" do
6
- expect(subject).not_to be_logged_in
6
+ it "should not login" do
7
+ expect_any_instance_of(described_class).not_to receive(:login)
8
+ described_class.new
7
9
  end
8
10
  end
9
- describe "with a block", :vcr do
10
- it "should be logged in" do
11
+ describe "with a block", type: :feature do
12
+ it "should wrap the block in a session" do
13
+ expect_any_instance_of(described_class).to receive(:login).and_call_original
14
+ expect_any_instance_of(described_class).to receive(:logout).and_call_original
11
15
  described_class.new do |client|
12
- expect(client).to be_logged_in
16
+ expect(client.session).to be_open
13
17
  end
14
18
  end
15
19
  end
16
20
  end
17
- describe "authentication", :vcr do
18
- describe "logging in" do
19
- before { subject.login }
20
- it "should be logged in" do
21
- expect(subject).to be_logged_in
21
+
22
+ describe "authentication", type: :feature do
23
+ describe "#login" do
24
+ it "should open a session" do
25
+ expect(subject.session).to be_closed
26
+ subject.login
27
+ expect(subject.session).to be_open
22
28
  end
23
29
  end
24
- describe "logging out" do
30
+ describe "#logout" do
25
31
  before { subject.login }
26
- it "should not be logged in" do
32
+ it "should close the session" do
33
+ expect(subject.session).to be_open
27
34
  subject.logout
28
- expect(subject).not_to be_logged_in
35
+ expect(subject.session).to be_closed
36
+ end
37
+ end
38
+ describe "without a session" do
39
+ it "should send the user name and password" do
40
+ expect_any_instance_of(Net::HTTP::Post).to receive(:basic_auth).with(subject.user, subject.password).and_call_original
41
+ subject.mint_identifier(ARK_SHOULDER)
29
42
  end
30
43
  end
31
44
  end
32
- describe "creating an identifier", :vcr do
33
- # TODO
45
+
46
+ describe "#create_identifier" do
47
+ let(:id) { "ark:/99999/fk4fn19h88" }
48
+ let(:http_response) { double(body: "success: ark:/99999/fk4fn19h88") }
49
+ let(:stub_response) { Response.new(http_response) }
50
+ before do
51
+ allow(Request).to receive(:execute) { stub_response }
52
+ end
53
+ subject { described_class.new.create_identifier(id) }
54
+ it "should be a success" do
55
+ expect(subject).to be_success
56
+ expect(subject.id).to eq(id)
57
+ end
34
58
  end
35
- describe "minting an identifier", :vcr do
59
+
60
+ describe "#mint_identifier" do
61
+ before { allow(Request).to receive(:execute) { stub_response } }
36
62
  describe "which is an ARK" do
37
- it "should be a success" do
38
- response = subject.mint_identifier(ARK_SHOULDER)
39
- expect(response).to be_success
40
- expect(response.message).to match(/#{ARK_SHOULDER}/)
63
+ let(:stub_response) { Response.new(double(body: "success: ark:/99999/fk4fn19h88")) }
64
+ subject { described_class.new.mint_identifier(ARK_SHOULDER) }
65
+ it "should be a succes" do
66
+ expect(subject).to be_success
67
+ expect(subject.id).to eq("ark:/99999/fk4fn19h88")
41
68
  end
42
69
  end
43
70
  describe "which is a DOI" do
44
- it "should be a sucess" do
45
- response = subject.mint_identifier(DOI_SHOULDER, doi_metadata)
46
- expect(response).to be_success
47
- expect(response.message).to match(/#{DOI_SHOULDER}/)
48
- expect(response.message).to match(/\| ark:/)
71
+ let(:http_response) { double(body: "success: doi:10.5072/FK2TEST | ark:/99999/fk4fn19h88") }
72
+ let(:stub_response) { Response.new(http_response) }
73
+ let(:metadata) do
74
+ <<-EOS
75
+ datacite.title: Test
76
+ datacite.creator: Duke
77
+ datacite.publisher: Duke
78
+ datacite.publicationyear: 2014
79
+ datacite.resourcetype: Other
80
+ EOS
81
+ end
82
+ subject { described_class.new.mint_identifier(DOI_SHOULDER, metadata) }
83
+ it "should be a sucess" do
84
+ expect(subject).to be_success
85
+ expect(subject.id).to eq("doi:10.5072/FK2TEST")
86
+ expect(subject.shadow_ark).to eq("ark:/99999/fk4fn19h88")
49
87
  end
50
88
  end
51
89
  end
52
- describe "getting identifier metadata" do
90
+
91
+ describe "#get_identifier_metadata" do
92
+ let(:stub_response) do
93
+ Response.new(double(body: <<-EOS
94
+ success: ark:/99999/fk4fn19h88
95
+ _updated: 1416507086
96
+ _target: http://ezid.cdlib.org/id/ark:/99999/fk4fn19h88
97
+ _profile: erc
98
+ _ownergroup: apitest
99
+ _owner: apitest
100
+ _export: yes
101
+ _created: 1416507086
102
+ _status: public
103
+ EOS
104
+ ))
105
+ end
53
106
  before do
54
- @identifier = subject.mint_identifier(ARK_SHOULDER).identifier
107
+ allow(Request).to receive(:execute) { stub_response }
55
108
  end
56
- it "should return the metadata" do
57
- response = subject.get_identifier_metadata(@identifier)
58
- expect(response.body).to match(/_status: public/)
109
+ subject { described_class.new.get_identifier_metadata("ark:/99999/fk4fn19h88") }
110
+ it "should retrieve the metadata" do
111
+ expect(subject.metadata).to eq <<-EOS
112
+ _updated: 1416507086
113
+ _target: http://ezid.cdlib.org/id/ark:/99999/fk4fn19h88
114
+ _profile: erc
115
+ _ownergroup: apitest
116
+ _owner: apitest
117
+ _export: yes
118
+ _created: 1416507086
119
+ _status: public
120
+ EOS
59
121
  end
60
122
  end
61
- describe "modifying an identifier" do
123
+
124
+ describe "#modify_identifier", type: :feature do
62
125
  before do
63
- @identifier = subject.mint_identifier(ARK_SHOULDER).identifier
126
+ @id = described_class.new.mint_identifier(ARK_SHOULDER).id
64
127
  end
128
+ subject { described_class.new.modify_identifier(@id, "dc.title" => "Test") }
65
129
  it "should update the metadata" do
66
- subject.modify_identifier(@identifier, "dc.title" => "Test")
67
- response = subject.get_identifier_metadata(@identifier)
68
- expect(response.body).to match(/dc.title: Test/)
130
+ expect(subject).to be_success
131
+ response = described_class.new.get_identifier_metadata(@id)
132
+ expect(response.metadata).to match(/dc.title: Test/)
69
133
  end
70
134
  end
71
- describe "deleting an identifier" do
135
+
136
+ describe "#delete_identifier", type: :feature do
72
137
  before do
73
- @identifier = subject.mint_identifier(ARK_SHOULDER, "_status" => "reserved").identifier
138
+ @id = described_class.new.mint_identifier(ARK_SHOULDER, "_status" => "reserved").id
74
139
  end
140
+ subject { described_class.new.delete_identifier(@id) }
75
141
  it "should delete the identifier" do
76
- response = subject.delete_identifier(@identifier)
77
- expect(response).to be_success
78
- expect { subject.get_identifier_metadata(@identifier) }.to raise_error
142
+ expect(subject).to be_success
143
+ expect { described_class.new.get_identifier_metadata(@id) }.to raise_error
79
144
  end
80
145
  end
81
- describe "server status", :vcr do
146
+
147
+ describe "server status" do
148
+ let(:http_response) do
149
+ double(body: <<-EOS
150
+ success: EZID is up
151
+ noid: up
152
+ ldap: up
153
+ EOS
154
+ )
155
+ end
156
+ let(:stub_response) { Response.new(http_response) }
157
+ before do
158
+ allow(Request).to receive(:execute) { stub_response }
159
+ end
160
+ subject { described_class.new.server_status("*") }
82
161
  it "should report the status of EZID and subsystems" do
83
- response = subject.server_status("*")
84
- expect(response).to be_success
85
- expect(response.message).to eq("EZID is up")
162
+ expect(subject).to be_success
163
+ expect(subject).to be_up
164
+ expect(subject.message).to eq("EZID is up")
165
+ expect(subject.noid).to eq("up")
166
+ expect(subject.ldap).to eq("up")
167
+ expect(subject.datacite).to eq("not checked")
168
+ end
169
+ end
170
+
171
+ describe "error handling" do
172
+ let(:stub_response) { Response.new(body: "error: bad request - no such identifier") }
173
+ before do
174
+ allow(Request).to receive(:execute) { stub_response }
175
+ end
176
+ it "should raise an exception" do
177
+ expect { subject.get_identifier_metadata("invalid") }.to raise_error
86
178
  end
87
179
  end
88
180
  end
89
181
  end
90
-