archivesspace-client 0.5.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d62f24f62e27e4dcb265982a09d19929029a12d7926983137f7835b9e945ec8d
4
- data.tar.gz: 88b7f1d79f02207cba4dfbb6334fae4a7080adca8634c6c52b53d6616398dfd0
3
+ metadata.gz: b567aa6c6f40826a102d43a028600f562d8842f791d8f26269668eaf23757c95
4
+ data.tar.gz: 318036e1c02449bf25265431d44baa2d765970f782cc836daa9db25bd2ca3f89
5
5
  SHA512:
6
- metadata.gz: d776656385e5e84c4ba0adfec158e6a43ef60720aec43108a15e6e307adaf5022eb2168d463cdc74f4657aae62bbea80662d851dc65aee8a4ed02b1f9cc432d2
7
- data.tar.gz: 482eeab5c389e3433bae0102a687dfc42ede31792bf0b9bafcd2b0ec22492769523c42639718f9c202fc83abd2b08b6b798a7827826441c2ee951de9d817eda9
6
+ metadata.gz: 10bae2e2c595663d40ec0629444639acd5c3750ea866a3dc0623152a5598818b33d2d511346d702759cbab9948941fc7fe9b4f0208cabd0c17cbee66d3b52ea3
7
+ data.tar.gz: 000440d2cfeed3c329c08b37040a3b1be15189f41d69afc0ff54e9426915c70165122d320687ad32eae1fc7277fa1321bc22b8cc26e480b8cb166bc03b213868
data/README.md CHANGED
@@ -4,22 +4,22 @@ Interact with ArchivesSpace via the API.
4
4
 
5
5
  <!-- TOC start (generated with https://github.com/derlin/bitdowntoc) -->
6
6
 
7
- * [Installation](#installation)
8
- * [Usage](#usage)
9
- * [Configuring a client](#configuring-a-client)
10
- * [Default configuration](#default-configuration)
11
- * [Custom configuration, on the fly](#custom-configuration-on-the-fly)
12
- * [Custom configuration, stored for use with CLI or console](#custom-configuration-stored-for-use-with-cli-or-console)
13
- * [Making basic requests](#making-basic-requests)
14
- * [Setting a repository context](#setting-a-repository-context)
15
- * [Templates](#templates)
16
- * [CLI](#cli)
17
- * [Console usage](#console-usage)
18
- * [Development](#development)
19
- * [Publishing](#publishing)
20
- * [Changelog](#changelog)
21
- * [Contributing](#contributing)
22
- * [License](#license)
7
+ - [Installation](#installation)
8
+ - [Usage](#usage)
9
+ - [Configuring a client](#configuring-a-client)
10
+ - [Default configuration](#default-configuration)
11
+ - [Custom configuration, on the fly](#custom-configuration-on-the-fly)
12
+ - [Custom configuration, stored for use with CLI or console](#custom-configuration-stored-for-use-with-cli-or-console)
13
+ - [Making basic requests](#making-basic-requests)
14
+ - [Setting a repository context](#setting-a-repository-context)
15
+ - [Templates](#templates)
16
+ - [CLI](#cli)
17
+ - [Console usage](#console-usage)
18
+ - [Development](#development)
19
+ - [Publishing](#publishing)
20
+ - [Changelog](#changelog)
21
+ - [Contributing](#contributing)
22
+ - [License](#license)
23
23
 
24
24
  <!-- TOC end -->
25
25
 
@@ -145,7 +145,8 @@ end
145
145
  ```
146
146
 
147
147
  Scopes restore the previous context on exit (even if the block raises) and can
148
- be nested:
148
+ be nested. Call `repository` with no id to nest a global-scope block inside a
149
+ repository scope:
149
150
 
150
151
  ```ruby
151
152
  client.repository(2) do
@@ -153,6 +154,11 @@ client.repository(2) do
153
154
  client.resources # scoped to repo 3
154
155
  end
155
156
  client.resources # back to repo 2
157
+
158
+ client.repository do
159
+ client.get('repositories') # global scope
160
+ end
161
+ client.resources # back to repo 2 again
156
162
  end
157
163
 
158
164
  # now back in the global scope
@@ -260,19 +266,33 @@ main/master branch a new release will be built and published.
260
266
 
261
267
  ## Changelog
262
268
 
269
+ ### 0.7.0
270
+
271
+ - Add support for multipart POST requests, including file uploads to the
272
+ `jobs_with_files` endpoint.
273
+
274
+ ### 0.6.0
275
+
276
+ - Support global scope within a nested context. This makes it much
277
+ more ergonomic to request uris within a repository context.
278
+
279
+ ### 0.5.1
280
+
281
+ - Fix: prevent caller options passed to request from being mutated.
282
+
263
283
  ### 0.5.0
264
284
 
265
285
  Breaking changes:
266
286
 
267
- * Removed the `base_repo` configuration option. Use `client.repository(id)` to
287
+ - Removed the `base_repo` configuration option. Use `client.repository(id)` to
268
288
  scope requests to a repository instead, either persistently or with a
269
289
  block that auto-restores the previous scope.
270
- * `client.repository(id)` with a block now saves and restores the previous
290
+ - `client.repository(id)` with a block now saves and restores the previous
271
291
  context (including when nested or when the block raises), instead of always
272
292
  resetting to the global scope.
273
- * Login failure now raises `ArchivesSpace::AuthenticationError` (was
293
+ - Login failure now raises `ArchivesSpace::AuthenticationError` (was
274
294
  `ConnectionError`). `ConnectionError` has been removed.
275
- * `Client.new` raises `ArchivesSpace::ConfigurationError` (was `RuntimeError`)
295
+ - `Client.new` raises `ArchivesSpace::ConfigurationError` (was `RuntimeError`)
276
296
  when given a non-`Configuration` argument.
277
297
 
278
298
  ## Contributing
@@ -29,8 +29,8 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency "vcr", "~> 6.3"
30
30
  spec.add_development_dependency "webmock", "~> 3.24"
31
31
 
32
- spec.add_dependency "dry-cli", "~> 0.7"
33
- spec.add_dependency "httparty", "~> 0.14"
32
+ spec.add_dependency "dry-cli", "< 2.0"
33
+ spec.add_dependency "httparty", "~> 0.16"
34
34
  spec.add_dependency "json", "~> 2.0"
35
35
  spec.add_dependency "jbuilder", "~> 2.12"
36
36
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
4
+ require "archivesspace/client"
5
+
6
+ # usage: ruby examples/upload_ead.rb path/to/ead.xml
7
+ path = ARGV.fetch(0) { abort "usage: ruby #{$PROGRAM_NAME} path/to/ead.xml" }
8
+
9
+ # official sandbox, config boilerplate
10
+ config = ArchivesSpace::Configuration.new(
11
+ {
12
+ base_uri: "https://test.archivesspace.org/staff/api",
13
+ username: "admin",
14
+ password: "admin",
15
+ page_size: 50,
16
+ throttle: 0,
17
+ verify_ssl: false
18
+ }
19
+ )
20
+
21
+ client = ArchivesSpace::Client.new(config).login
22
+
23
+ job = {
24
+ jsonmodel_type: "job",
25
+ job: {
26
+ jsonmodel_type: "import_job",
27
+ import_type: "ead_xml",
28
+ filenames: [File.basename(path)]
29
+ }
30
+ }
31
+
32
+ client.repository 2 do
33
+ response = File.open(path) do |file|
34
+ client.post_multipart("jobs_with_files", {job: job.to_json, files: [file]})
35
+ end
36
+ abort "upload failed (#{response.status_code}): #{response.body}" unless
37
+ response.status_code == 200
38
+
39
+ job_id = response.parsed["id"]
40
+ puts "created import job #{job_id}"
41
+
42
+ # poll until the import finishes, but not for more than ~60s
43
+ status = nil
44
+ 30.times do
45
+ status = client.get("jobs/#{job_id}").parsed["status"]
46
+ puts "status: #{status}"
47
+ break if %w[completed failed canceled].include?(status)
48
+
49
+ sleep 2
50
+ end
51
+
52
+ puts client.get("jobs/#{job_id}/log").body
53
+ end
@@ -33,6 +33,12 @@ module ArchivesSpace
33
33
  request "POST", path, {body: payload.to_json, query: params}
34
34
  end
35
35
 
36
+ # Allow passing a file object (multipart = true)
37
+ def post_multipart(path, payload, params = {})
38
+ body = payload.transform_values { |v| v.is_a?(Hash) ? v.to_json : v }
39
+ request "POST", path, {body: body, query: params, multipart: true}
40
+ end
41
+
36
42
  def put(path, payload, params = {})
37
43
  request "PUT", path, {body: payload.to_json, query: params}
38
44
  end
@@ -42,18 +48,22 @@ module ArchivesSpace
42
48
  end
43
49
 
44
50
  # Scoping requests
45
- def repository(id)
46
- return use_global_repository if id.nil?
47
-
48
- begin
49
- Integer(id)
50
- rescue ArgumentError, TypeError
51
- raise RepositoryIdError, "Invalid Repository id: #{id}"
51
+ def repository(id = nil)
52
+ if id.nil?
53
+ return use_global_repository unless block_given?
54
+
55
+ new_context = nil
56
+ else
57
+ begin
58
+ Integer(id)
59
+ rescue ArgumentError, TypeError
60
+ raise RepositoryIdError, "Invalid Repository id: #{id}"
61
+ end
62
+
63
+ new_context = "repositories/#{id}"
64
+ return @context = new_context unless block_given?
52
65
  end
53
66
 
54
- new_context = "repositories/#{id}"
55
- return @context = new_context unless block_given?
56
-
57
67
  previous = @context
58
68
  @context = new_context
59
69
  begin
@@ -72,7 +82,7 @@ module ArchivesSpace
72
82
  def request(method, path, options = {})
73
83
  sleep config.throttle
74
84
  options = options.dup
75
- options[:headers] = {TOKEN => token} if token
85
+ options[:headers] = options.fetch(:headers, {}).merge(TOKEN => token) if token
76
86
  Request.new(context, config, method, path, options).execute
77
87
  end
78
88
  end
@@ -17,6 +17,7 @@ module ArchivesSpace
17
17
  @options = options.dup
18
18
 
19
19
  @options[:headers] = DEFAULT_HEADERS.merge(@options.fetch(:headers, {}))
20
+ @options[:headers].delete("Content-Type") if @options[:multipart]
20
21
  @options[:headers]["User-Agent"] = "#{Client::NAME}/#{Client::VERSION}"
21
22
 
22
23
  @options[:verify] = config.verify_ssl
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ArchivesSpace
4
4
  class Client
5
- VERSION = "0.5.1"
5
+ VERSION = "0.7.0"
6
6
  end
7
7
  end
@@ -141,6 +141,39 @@ describe ArchivesSpace::Client do
141
141
  expect(client.context).to eq "repositories/2"
142
142
  end
143
143
 
144
+ it "scopes to the global repository within a block and restores afterwards" do
145
+ client.repository 2
146
+ client.repository(nil) do
147
+ expect(client.context).to be_nil
148
+ end
149
+ expect(client.context).to eq "repositories/2"
150
+ end
151
+
152
+ it "scopes to the global repository when called with no id and a block" do
153
+ client.repository 2
154
+ client.repository do
155
+ expect(client.context).to be_nil
156
+ end
157
+ expect(client.context).to eq "repositories/2"
158
+ end
159
+
160
+ it "nests a no-arg global block inside a repository block and restores both contexts" do
161
+ client.repository(2) do
162
+ expect(client.context).to eq "repositories/2"
163
+ client.repository do
164
+ expect(client.context).to be_nil
165
+ end
166
+ expect(client.context).to eq "repositories/2"
167
+ end
168
+ expect(client.context).to be_nil
169
+ end
170
+
171
+ it "restores the previous repository context even if a nil-scoped block raises" do
172
+ client.repository 2
173
+ expect { client.repository(nil) { raise "boom" } }.to raise_error("boom")
174
+ expect(client.context).to eq "repositories/2"
175
+ end
176
+
144
177
  it "restores the context even if the block raises" do
145
178
  expect { client.repository(2) { raise "boom" } }.to raise_error("boom")
146
179
  expect(client.context).to be_nil
@@ -176,6 +209,24 @@ describe ArchivesSpace::Client do
176
209
  client.get("resources", opts)
177
210
  expect(opts).to eq(caller_opts)
178
211
  end
212
+
213
+ it "posts a file (as multipart form data) with session token" do
214
+ client.token = "abc123"
215
+ stub = stub_request(:post, "#{DEFAULT_BASE_URI}/repositories/2/jobs_with_files")
216
+ .with { |req|
217
+ req.headers["Content-Type"].start_with?("multipart/form-data; boundary=") &&
218
+ req.headers["X-Archivesspace-Session"] == "abc123" &&
219
+ req.body.include?('name="files[]"; filename="client_spec.rb"') &&
220
+ req.body.include?('name="job"')
221
+ }
222
+ .to_return(status: 200, body: "{}", headers: {"Content-Type" => "application/json"})
223
+ File.open(__FILE__) do |file|
224
+ client.repository(2) do
225
+ client.post_multipart("jobs_with_files", {job: {jsonmodel_type: "job"}, files: [file]})
226
+ end
227
+ end
228
+ expect(stub).to have_been_requested
229
+ end
179
230
  end
180
231
 
181
232
  describe "URL construction" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: archivesspace-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Cooper
@@ -145,30 +145,30 @@ dependencies:
145
145
  name: dry-cli
146
146
  requirement: !ruby/object:Gem::Requirement
147
147
  requirements:
148
- - - "~>"
148
+ - - "<"
149
149
  - !ruby/object:Gem::Version
150
- version: '0.7'
150
+ version: '2.0'
151
151
  type: :runtime
152
152
  prerelease: false
153
153
  version_requirements: !ruby/object:Gem::Requirement
154
154
  requirements:
155
- - - "~>"
155
+ - - "<"
156
156
  - !ruby/object:Gem::Version
157
- version: '0.7'
157
+ version: '2.0'
158
158
  - !ruby/object:Gem::Dependency
159
159
  name: httparty
160
160
  requirement: !ruby/object:Gem::Requirement
161
161
  requirements:
162
162
  - - "~>"
163
163
  - !ruby/object:Gem::Version
164
- version: '0.14'
164
+ version: '0.16'
165
165
  type: :runtime
166
166
  prerelease: false
167
167
  version_requirements: !ruby/object:Gem::Requirement
168
168
  requirements:
169
169
  - - "~>"
170
170
  - !ruby/object:Gem::Version
171
- version: '0.14'
171
+ version: '0.16'
172
172
  - !ruby/object:Gem::Dependency
173
173
  name: json
174
174
  requirement: !ruby/object:Gem::Requirement
@@ -223,6 +223,7 @@ files:
223
223
  - examples/templates.rb
224
224
  - examples/test_connection.rb
225
225
  - examples/update_feed.rb
226
+ - examples/upload_ead.rb
226
227
  - examples/user_groups.rb
227
228
  - exe/asclient
228
229
  - features/exec.feature
@@ -269,7 +270,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
269
270
  - !ruby/object:Gem::Version
270
271
  version: '0'
271
272
  requirements: []
272
- rubygems_version: 4.0.10
273
+ rubygems_version: 4.0.22
273
274
  specification_version: 4
274
275
  summary: Interact with ArchivesSpace via the API.
275
276
  test_files: []