atlas_rb 1.9.2 → 1.9.4
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 +4 -4
- data/.version +1 -1
- data/CHANGELOG.md +80 -0
- data/Gemfile.lock +1 -1
- data/README.md +42 -0
- data/lib/atlas_rb/blob.rb +21 -8
- data/lib/atlas_rb/collection.rb +49 -13
- data/lib/atlas_rb/community.rb +43 -11
- data/lib/atlas_rb/compilation.rb +48 -16
- data/lib/atlas_rb/errors.rb +83 -16
- data/lib/atlas_rb/file_set.rb +18 -6
- data/lib/atlas_rb/middleware/raise_on_resource_error.rb +41 -9
- data/lib/atlas_rb/person.rb +24 -8
- data/lib/atlas_rb/resource.rb +65 -0
- data/lib/atlas_rb/work.rb +60 -20
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 808bf4695fcba808f36a2c808885529bdff8b8dae50ddfed71cd605115aa9014
|
|
4
|
+
data.tar.gz: 532e5bac353cceeff1806829f4e8d8f2421051799b3a249bf3119c4001c144fc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 177bfa1e9c486473285eba7fac243719f08af97ad58999fdb08c9d08725981ba8dce66143943768ff12a8e08d994ca34eb02a791cd567b7f98294732fd461a2a
|
|
7
|
+
data.tar.gz: 0eba14aa8d5ac54b4153fd48810c90fb979ac608cc3bf2b0fb68bdb039ed9b4db763d6610839a0656374f684fc9821bde4d3f7ae336f58ac9f894aa4aee2ad81
|
data/.version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.9.
|
|
1
|
+
1.9.4
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,85 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.4
|
|
4
|
+
|
|
5
|
+
### Fixed — write bindings parsed the response body without checking the status
|
|
6
|
+
|
|
7
|
+
A write aimed at a resource that is not there raised
|
|
8
|
+
`JSON::ParserError: unexpected end of input at line 1 column 1`. Atlas is
|
|
9
|
+
correct here: it answers `head :not_found`, a `404` with an empty body. The
|
|
10
|
+
bindings then handed that empty body to `JSON.parse`.
|
|
11
|
+
|
|
12
|
+
The message named neither the verb nor the resource, so an operator whose XML
|
|
13
|
+
manifest carried a PID from another environment got five identical parser
|
|
14
|
+
errors and nothing to act on. `Resource.fetch_resource` already guarded the
|
|
15
|
+
**read** path against exactly this; the write half never got the same
|
|
16
|
+
treatment.
|
|
17
|
+
|
|
18
|
+
Every `create` / `update` / `metadata` / `parent` / `rollback` and sibling
|
|
19
|
+
across `Work`, `Collection`, `Community`, `Blob`, `FileSet`, `Compilation` and
|
|
20
|
+
`Person` now goes through a `write_resource` companion:
|
|
21
|
+
|
|
22
|
+
- `404` → {AtlasRb::NotFoundError}, naming the verb and path.
|
|
23
|
+
- `410` → the parsed body, as on the read path: an Idempotency-Key replay whose
|
|
24
|
+
resource has since been tombstoned answers `410 Gone` *with* the tombstone.
|
|
25
|
+
- any other non-2xx → {AtlasRb::ResourceError}, carrying Atlas's status + body.
|
|
26
|
+
- `2xx` → the parsed body, exactly as before.
|
|
27
|
+
|
|
28
|
+
`NotFoundError` subclasses `ResourceError`, so a caller that only wants "the
|
|
29
|
+
write failed" rescues the parent.
|
|
30
|
+
|
|
31
|
+
Note the deliberate asymmetry with the read path, which keeps returning `nil`
|
|
32
|
+
on a `404`: that answers a read, but not a write — the caller asked for a
|
|
33
|
+
change and did not get one.
|
|
34
|
+
|
|
35
|
+
Blast radius is which exception surfaces, not whether one does. The typed
|
|
36
|
+
`403` / `422` translations raise inside the Faraday stack, so they still fire
|
|
37
|
+
first; a `422` the middleware passes through on purpose (`tombstone`'s
|
|
38
|
+
`has_live_children`) is unaffected, because `tombstone`, `destroy` and
|
|
39
|
+
`complete` return the raw response and never parsed.
|
|
40
|
+
|
|
41
|
+
## 1.9.3
|
|
42
|
+
|
|
43
|
+
### Added — `depositor:` on `Collection.create` and `Community.create`
|
|
44
|
+
|
|
45
|
+
`AtlasRb::Collection.create(parent_id, depositor: "000000099")` (and the same
|
|
46
|
+
on `Community.create`) stamps a container's intellectual owner, matching what
|
|
47
|
+
`Work.create` has always supported. Atlas already read the param; only the
|
|
48
|
+
bindings couldn't pass it.
|
|
49
|
+
|
|
50
|
+
The case it unblocks is seeding an institutional tree: the caller acts as an
|
|
51
|
+
admin (the only identity whose wildcard carries a whole seed sequence) while
|
|
52
|
+
attributing the containers to the anonymous NUID, since nobody personally owns
|
|
53
|
+
them and access is via Grouper groups. That matters now that a depositor
|
|
54
|
+
carries edit rights on their own resource — a container stamped with a real
|
|
55
|
+
person's NUID would hand them edit over that whole subtree.
|
|
56
|
+
|
|
57
|
+
Omitted, the depositor still falls through to the acting user, so existing
|
|
58
|
+
callers are unaffected.
|
|
59
|
+
|
|
60
|
+
### Added — typed errors for Atlas's container-create `403` and ACL-write `422`
|
|
61
|
+
|
|
62
|
+
Atlas gained two refusals that the bindings previously swallowed, because
|
|
63
|
+
`RaiseOnResourceError` was scoped to the re-parent / linked-member /
|
|
64
|
+
Compilation / derivative-permissions / upload paths:
|
|
65
|
+
|
|
66
|
+
- **A refused create.** `POST /{works,collections,communities}` now `403`s when
|
|
67
|
+
the caller holds no edit rights on the destination container. The binding's
|
|
68
|
+
`["collection"]` unwrap returned `nil`, so the caller's next `.id` raised
|
|
69
|
+
`NoMethodError` — an unhandled 500 instead of an authorization failure. Now
|
|
70
|
+
{AtlasRb::ForbiddenError}, carrying `action: "create_child"` and the parent
|
|
71
|
+
container's class as `subject`.
|
|
72
|
+
- **A refused ACL write.** `PATCH /{type}/:id` with `metadata[permissions]`
|
|
73
|
+
now `422`s with `visibility_exceeds_parent` when the read audience would
|
|
74
|
+
exceed the structural container. The parsed envelope looked like a success
|
|
75
|
+
payload, so a user's visibility edit was discarded silently. Now
|
|
76
|
+
{AtlasRb::PermissionsError}, keyed on the `error` code rather than the path —
|
|
77
|
+
the same endpoint's other `422`s (tombstone's `has_live_children`) still pass
|
|
78
|
+
through untouched, which a path rule could not distinguish.
|
|
79
|
+
|
|
80
|
+
The create branch matches the three paths exactly and only on `POST`, so member
|
|
81
|
+
actions under the same prefix and the index `GET` are unaffected.
|
|
82
|
+
|
|
3
83
|
## 1.9.2
|
|
4
84
|
|
|
5
85
|
### Added — `read_only:` on `System::Token.mint`
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -222,6 +222,11 @@ AtlasRb::FileSet.create("w-789", "primary") # file_set under work w-789, classif
|
|
|
222
222
|
AtlasRb::Blob.create("w-789", path, name) # blob under work w-789 with original filename preserved
|
|
223
223
|
```
|
|
224
224
|
|
|
225
|
+
`Community.create`, `Collection.create`, and `Work.create` each accept an
|
|
226
|
+
optional `depositor:` kwarg — the NUID to stamp as the resource's intellectual
|
|
227
|
+
owner, independent of who authorizes the call. Omitted, Atlas falls through to
|
|
228
|
+
the acting user.
|
|
229
|
+
|
|
225
230
|
`Work.create`, `FileSet.create`, and `Blob.create` each accept an optional
|
|
226
231
|
`idempotency_key:` kwarg for retry-safe bulk-deposit jobs. The caller
|
|
227
232
|
generates the UUID; the Atlas server enforces uniqueness scoped to the
|
|
@@ -433,6 +438,43 @@ tombstoned resources come back flagged (`"tombstoned" => true`), so index
|
|
|
433
438
|
by `"noid"` rather than assuming positional correspondence. NOIDs only —
|
|
434
439
|
raw Valkyrie ids are not a supported input.
|
|
435
440
|
|
|
441
|
+
### Missing resources: `nil` on a read, a raise on a write
|
|
442
|
+
|
|
443
|
+
The two halves of the API answer an absent resource differently, on purpose.
|
|
444
|
+
|
|
445
|
+
A **read** returns `nil`. "There is nothing there" is a legitimate answer to a
|
|
446
|
+
question, and callers already nil-check a `find`:
|
|
447
|
+
|
|
448
|
+
```ruby
|
|
449
|
+
AtlasRb::Work.find("doesnotexist") # => nil
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
A **write** raises `AtlasRb::NotFoundError`. The caller asked for a change and
|
|
453
|
+
did not get one, so `nil` would invite the silent failure the read guard was
|
|
454
|
+
written to prevent:
|
|
455
|
+
|
|
456
|
+
```ruby
|
|
457
|
+
begin
|
|
458
|
+
AtlasRb::Work.update("doesnotexist", "/tmp/mods.xml")
|
|
459
|
+
rescue AtlasRb::NotFoundError => e
|
|
460
|
+
e.status # => 404
|
|
461
|
+
e.message # => "PATCH /works/doesnotexist → 404 (no such resource)"
|
|
462
|
+
end
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
This covers every `create` / `update` / `metadata` / `parent` / `rollback` and
|
|
466
|
+
their siblings on `Work`, `Collection`, `Community`, `Blob`, `FileSet`,
|
|
467
|
+
`Compilation` and `Person`. Any other non-2xx a write gets raises
|
|
468
|
+
`AtlasRb::ResourceError`, which `NotFoundError` subclasses — so a caller that
|
|
469
|
+
only wants "the write failed" rescues the parent.
|
|
470
|
+
|
|
471
|
+
Two neighbours are deliberately untouched. A `410 Gone` is returned, not
|
|
472
|
+
raised, the same way a read returns it: an `Idempotency-Key` replay whose
|
|
473
|
+
resource has since been tombstoned answers `410` *with* the tombstone, and the
|
|
474
|
+
caller reads `tombstoned`. And the typed `403` / `422` translations above still
|
|
475
|
+
fire first, while `tombstone` / `destroy` / `complete` keep returning the raw
|
|
476
|
+
Faraday response for the caller to read.
|
|
477
|
+
|
|
436
478
|
## End-to-end example
|
|
437
479
|
|
|
438
480
|
JSON responses come back as `AtlasRb::Mash` (a `Hashie::Mash` subclass), so
|
data/lib/atlas_rb/blob.rb
CHANGED
|
@@ -157,6 +157,10 @@ module AtlasRb
|
|
|
157
157
|
# `"digest"` (the recorded fixity digest, `"sha512:<hex>"`).
|
|
158
158
|
# @raise [AtlasRb::FixityMismatchError] if `expected_digest` was supplied and
|
|
159
159
|
# the uploaded bytes did not match (or the algorithm is unsupported).
|
|
160
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
161
|
+
# resource, so the write did not happen.
|
|
162
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
163
|
+
# and body.
|
|
160
164
|
#
|
|
161
165
|
# @note Streams the file (FD closed deterministically); a multi-GB upload is
|
|
162
166
|
# not buffered in memory. See {AtlasRb::FaradayHelper#with_file_part}.
|
|
@@ -175,9 +179,9 @@ module AtlasRb
|
|
|
175
179
|
payload = { work_id: id, original_filename: original_filename, binary: part }
|
|
176
180
|
payload[:expected_digest] = expected_digest if expected_digest
|
|
177
181
|
|
|
178
|
-
AtlasRb::Mash.new(
|
|
182
|
+
AtlasRb::Mash.new(write_resource(
|
|
179
183
|
multipart(nuid, on_behalf_of: on_behalf_of, idempotency_key: idempotency_key)
|
|
180
|
-
.post(ROUTE, payload)
|
|
184
|
+
.post(ROUTE, payload)
|
|
181
185
|
))['blob']
|
|
182
186
|
end
|
|
183
187
|
end
|
|
@@ -223,6 +227,10 @@ module AtlasRb
|
|
|
223
227
|
# `"blob"`, with a refreshed `"digest"` for the new revision).
|
|
224
228
|
# @raise [AtlasRb::FixityMismatchError] if `expected_digest` was supplied and
|
|
225
229
|
# the uploaded bytes did not match (or the algorithm is unsupported).
|
|
230
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
231
|
+
# resource, so the write did not happen.
|
|
232
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
233
|
+
# and body.
|
|
226
234
|
#
|
|
227
235
|
# @note Streams the file with the FD closed deterministically — see {.create}.
|
|
228
236
|
#
|
|
@@ -236,9 +244,9 @@ module AtlasRb
|
|
|
236
244
|
payload = { binary: part }
|
|
237
245
|
payload[:expected_digest] = expected_digest if expected_digest
|
|
238
246
|
|
|
239
|
-
AtlasRb::Mash.new(
|
|
247
|
+
AtlasRb::Mash.new(write_resource(
|
|
240
248
|
multipart(nuid, on_behalf_of: on_behalf_of, idempotency_key: idempotency_key)
|
|
241
|
-
.patch(ROUTE + id, payload)
|
|
249
|
+
.patch(ROUTE + id, payload)
|
|
242
250
|
))
|
|
243
251
|
end
|
|
244
252
|
end
|
|
@@ -324,8 +332,9 @@ module AtlasRb
|
|
|
324
332
|
# recopied. Avoids a full round-trip of the bytes back through the caller
|
|
325
333
|
# (vs. re-streaming {.version_content} into {.update}).
|
|
326
334
|
#
|
|
327
|
-
# Pass a `version_id` obtained from {.versions}.
|
|
328
|
-
#
|
|
335
|
+
# Pass a `version_id` obtained from {.versions}. Atlas answers an unknown id
|
|
336
|
+
# or version with a `404`, which raises {AtlasRb::NotFoundError} — a write
|
|
337
|
+
# that did not happen must not read like one that did.
|
|
329
338
|
#
|
|
330
339
|
# @param id [String] the Blob ID.
|
|
331
340
|
# @param version_id [String] the OCFL version label to reinstate, e.g. `"v1"`.
|
|
@@ -337,13 +346,17 @@ module AtlasRb
|
|
|
337
346
|
# omitted.
|
|
338
347
|
# @return [AtlasRb::Mash] the updated `"blob"` payload (NOID unchanged,
|
|
339
348
|
# `"digest"` refreshed to the reinstated bytes).
|
|
349
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
350
|
+
# resource, so the write did not happen.
|
|
351
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
352
|
+
# and body.
|
|
340
353
|
#
|
|
341
354
|
# @example
|
|
342
355
|
# AtlasRb::Blob.rollback("b-321", "v1")
|
|
343
356
|
def self.rollback(id, version_id, nuid: nil, on_behalf_of: nil)
|
|
344
|
-
AtlasRb::Mash.new(
|
|
357
|
+
AtlasRb::Mash.new(write_resource(
|
|
345
358
|
connection({}, nuid, on_behalf_of: on_behalf_of)
|
|
346
|
-
.post("#{ROUTE}#{id}/rollback", JSON.dump(version_id: version_id))
|
|
359
|
+
.post("#{ROUTE}#{id}/rollback", JSON.dump(version_id: version_id))
|
|
347
360
|
))['blob']
|
|
348
361
|
end
|
|
349
362
|
end
|
data/lib/atlas_rb/collection.rb
CHANGED
|
@@ -57,14 +57,30 @@ module AtlasRb
|
|
|
57
57
|
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
58
58
|
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
59
59
|
# omitted.
|
|
60
|
+
# @param depositor [String, nil] NUID to stamp as the Collection's
|
|
61
|
+
# intellectual owner. Omit it and Atlas falls through to the acting user.
|
|
62
|
+
# Supply it to attribute a container to someone other than whoever is
|
|
63
|
+
# authorizing the call — e.g. seeding an institutional tree as an admin
|
|
64
|
+
# while attributing it to the anonymous NUID, since nobody personally owns
|
|
65
|
+
# those containers and access to them is via Grouper groups. The depositor
|
|
66
|
+
# is immutable post-create; there is no setter on the update surface.
|
|
60
67
|
# @return [Hash] the created Collection payload (post-update if
|
|
61
68
|
# `xml_path` was supplied).
|
|
69
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
70
|
+
# resource, so the write did not happen.
|
|
71
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
72
|
+
# and body.
|
|
62
73
|
#
|
|
63
74
|
# @example A featured showcase collection
|
|
64
75
|
# AtlasRb::Collection.create("c-123", featured: true)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
76
|
+
#
|
|
77
|
+
# @example An institutional container owned by nobody
|
|
78
|
+
# AtlasRb::Collection.create("c-123", depositor: "000000099")
|
|
79
|
+
def self.create(id, xml_path = nil, featured: false, nuid: nil, on_behalf_of: nil, depositor: nil)
|
|
80
|
+
params = { parent_id: id, featured: featured }
|
|
81
|
+
params[:depositor] = depositor if depositor
|
|
82
|
+
result = AtlasRb::Mash.new(write_resource(
|
|
83
|
+
connection(params, nuid, on_behalf_of: on_behalf_of).post(ROUTE)
|
|
68
84
|
))["collection"]
|
|
69
85
|
return result if xml_path.to_s.empty?
|
|
70
86
|
|
|
@@ -84,12 +100,16 @@ module AtlasRb
|
|
|
84
100
|
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
85
101
|
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when omitted.
|
|
86
102
|
# @return [AtlasRb::Mash] the updated `"collection"` object, already unwrapped.
|
|
103
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
104
|
+
# resource, so the write did not happen.
|
|
105
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
106
|
+
# and body.
|
|
87
107
|
#
|
|
88
108
|
# @example
|
|
89
109
|
# AtlasRb::Collection.set_featured("col-456", true)
|
|
90
110
|
def self.set_featured(id, featured, nuid: nil, on_behalf_of: nil)
|
|
91
|
-
AtlasRb::Mash.new(
|
|
92
|
-
connection({ featured: featured }, nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id)
|
|
111
|
+
AtlasRb::Mash.new(write_resource(
|
|
112
|
+
connection({ featured: featured }, nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id)
|
|
93
113
|
))["collection"]
|
|
94
114
|
end
|
|
95
115
|
|
|
@@ -122,13 +142,17 @@ module AtlasRb
|
|
|
122
142
|
# envelope's `error` code is exposed as `#code`.
|
|
123
143
|
# @raise [AtlasRb::ForbiddenError] if Atlas refuses the move on
|
|
124
144
|
# authorization grounds (HTTP 403).
|
|
145
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
146
|
+
# resource, so the write did not happen.
|
|
147
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
148
|
+
# and body.
|
|
125
149
|
#
|
|
126
150
|
# @example
|
|
127
151
|
# AtlasRb::Collection.reparent("col-456", "c-999")
|
|
128
152
|
def self.reparent(id, new_parent_id, nuid: nil, on_behalf_of: nil)
|
|
129
|
-
AtlasRb::Mash.new(
|
|
153
|
+
AtlasRb::Mash.new(write_resource(
|
|
130
154
|
connection({ parent_id: new_parent_id }, nuid, on_behalf_of: on_behalf_of)
|
|
131
|
-
.patch(ROUTE + id + '/parent')
|
|
155
|
+
.patch(ROUTE + id + '/parent')
|
|
132
156
|
))["collection"]
|
|
133
157
|
end
|
|
134
158
|
|
|
@@ -188,6 +212,10 @@ module AtlasRb
|
|
|
188
212
|
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
189
213
|
# omitted.
|
|
190
214
|
# @return [Hash] the parsed JSON response from the patch.
|
|
215
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
216
|
+
# resource, so the write did not happen.
|
|
217
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
218
|
+
# and body.
|
|
191
219
|
#
|
|
192
220
|
# @example
|
|
193
221
|
# AtlasRb::Collection.update("col-456", "/tmp/collection-mods.xml")
|
|
@@ -195,8 +223,8 @@ module AtlasRb
|
|
|
195
223
|
payload = { binary: Faraday::Multipart::FilePart.new(File.open(xml_path),
|
|
196
224
|
"application/xml",
|
|
197
225
|
File.basename(xml_path)) }
|
|
198
|
-
AtlasRb::Mash.new(
|
|
199
|
-
multipart(nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id, payload)
|
|
226
|
+
AtlasRb::Mash.new(write_resource(
|
|
227
|
+
multipart(nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id, payload)
|
|
200
228
|
))
|
|
201
229
|
end
|
|
202
230
|
|
|
@@ -216,12 +244,16 @@ module AtlasRb
|
|
|
216
244
|
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
217
245
|
# omitted.
|
|
218
246
|
# @return [Hash] the parsed JSON response.
|
|
247
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
248
|
+
# resource, so the write did not happen.
|
|
249
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
250
|
+
# and body.
|
|
219
251
|
#
|
|
220
252
|
# @example
|
|
221
253
|
# AtlasRb::Collection.metadata("col-456", title: "Renamed Collection")
|
|
222
254
|
def self.metadata(id, values, nuid: nil, on_behalf_of: nil)
|
|
223
|
-
AtlasRb::Mash.new(
|
|
224
|
-
connection({ metadata: values }, nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id)
|
|
255
|
+
AtlasRb::Mash.new(write_resource(
|
|
256
|
+
connection({ metadata: values }, nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id)
|
|
225
257
|
))
|
|
226
258
|
end
|
|
227
259
|
|
|
@@ -243,6 +275,10 @@ module AtlasRb
|
|
|
243
275
|
# @raise [AtlasRb::StaleResourceError] if Atlas reports an optimistic-lock
|
|
244
276
|
# conflict that exhausted its internal retry budget (HTTP 409 with
|
|
245
277
|
# `error: "stale_resource"`).
|
|
278
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
279
|
+
# resource, so the write did not happen.
|
|
280
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
281
|
+
# and body.
|
|
246
282
|
#
|
|
247
283
|
# @example
|
|
248
284
|
# AtlasRb::Collection.set_thumbnails(
|
|
@@ -253,9 +289,9 @@ module AtlasRb
|
|
|
253
289
|
# )
|
|
254
290
|
def self.set_thumbnails(id, thumbnail: nil, thumbnail_2x: nil, preview: nil, nuid: nil, on_behalf_of: nil)
|
|
255
291
|
body = { thumbnail: thumbnail, thumbnail_2x: thumbnail_2x, preview: preview }.compact
|
|
256
|
-
AtlasRb::Mash.new(
|
|
292
|
+
AtlasRb::Mash.new(write_resource(
|
|
257
293
|
connection({}, nuid, on_behalf_of: on_behalf_of)
|
|
258
|
-
.patch(ROUTE + id + '/thumbnails', JSON.dump(body))
|
|
294
|
+
.patch(ROUTE + id + '/thumbnails', JSON.dump(body))
|
|
259
295
|
))
|
|
260
296
|
end
|
|
261
297
|
|
data/lib/atlas_rb/community.rb
CHANGED
|
@@ -52,17 +52,33 @@ module AtlasRb
|
|
|
52
52
|
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
53
53
|
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
54
54
|
# omitted.
|
|
55
|
+
# @param depositor [String, nil] NUID to stamp as the Community's
|
|
56
|
+
# intellectual owner. Omit it and Atlas falls through to the acting user.
|
|
57
|
+
# Supply it to attribute a container to someone other than whoever is
|
|
58
|
+
# authorizing the call — e.g. seeding an institutional tree as an admin
|
|
59
|
+
# while attributing it to the anonymous NUID, since nobody personally owns
|
|
60
|
+
# those containers and access to them is via Grouper groups. The depositor
|
|
61
|
+
# is immutable post-create; there is no setter on the update surface.
|
|
55
62
|
# @return [Hash] the created Community payload (post-update if `xml_path`
|
|
56
63
|
# was supplied).
|
|
64
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
65
|
+
# resource, so the write did not happen.
|
|
66
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
67
|
+
# and body.
|
|
57
68
|
#
|
|
58
69
|
# @example Top-level community, no metadata
|
|
59
70
|
# AtlasRb::Community.create(nil)
|
|
60
71
|
#
|
|
61
72
|
# @example Sub-community seeded from MODS
|
|
62
73
|
# AtlasRb::Community.create("c-parent", "/tmp/dept-mods.xml")
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
74
|
+
#
|
|
75
|
+
# @example An institutional container owned by nobody
|
|
76
|
+
# AtlasRb::Community.create("c-parent", depositor: "000000099")
|
|
77
|
+
def self.create(id = nil, xml_path = nil, nuid: nil, on_behalf_of: nil, depositor: nil)
|
|
78
|
+
params = { parent_id: id }
|
|
79
|
+
params[:depositor] = depositor if depositor
|
|
80
|
+
result = AtlasRb::Mash.new(write_resource(
|
|
81
|
+
connection(params, nuid, on_behalf_of: on_behalf_of).post(ROUTE)
|
|
66
82
|
))["community"]
|
|
67
83
|
return result if xml_path.to_s.empty?
|
|
68
84
|
|
|
@@ -101,6 +117,10 @@ module AtlasRb
|
|
|
101
117
|
# `parent_not_found`). The envelope's `error` code is exposed as `#code`.
|
|
102
118
|
# @raise [AtlasRb::ForbiddenError] if Atlas refuses the move on
|
|
103
119
|
# authorization grounds (HTTP 403).
|
|
120
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
121
|
+
# resource, so the write did not happen.
|
|
122
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
123
|
+
# and body.
|
|
104
124
|
#
|
|
105
125
|
# @example Move under another Community
|
|
106
126
|
# AtlasRb::Community.reparent("c-123", "c-999")
|
|
@@ -108,9 +128,9 @@ module AtlasRb
|
|
|
108
128
|
# @example Promote to a top-level Community
|
|
109
129
|
# AtlasRb::Community.reparent("c-123", nil)
|
|
110
130
|
def self.reparent(id, new_parent_id, nuid: nil, on_behalf_of: nil)
|
|
111
|
-
AtlasRb::Mash.new(
|
|
131
|
+
AtlasRb::Mash.new(write_resource(
|
|
112
132
|
connection({ parent_id: new_parent_id }, nuid, on_behalf_of: on_behalf_of)
|
|
113
|
-
.patch(ROUTE + id + '/parent')
|
|
133
|
+
.patch(ROUTE + id + '/parent')
|
|
114
134
|
))["community"]
|
|
115
135
|
end
|
|
116
136
|
|
|
@@ -171,6 +191,10 @@ module AtlasRb
|
|
|
171
191
|
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
172
192
|
# omitted.
|
|
173
193
|
# @return [Hash] the parsed JSON response from the patch.
|
|
194
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
195
|
+
# resource, so the write did not happen.
|
|
196
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
197
|
+
# and body.
|
|
174
198
|
#
|
|
175
199
|
# @example
|
|
176
200
|
# AtlasRb::Community.update("c-123", "/tmp/community-mods.xml")
|
|
@@ -178,8 +202,8 @@ module AtlasRb
|
|
|
178
202
|
payload = { binary: Faraday::Multipart::FilePart.new(File.open(xml_path),
|
|
179
203
|
"application/xml",
|
|
180
204
|
File.basename(xml_path)) }
|
|
181
|
-
AtlasRb::Mash.new(
|
|
182
|
-
multipart(nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id, payload)
|
|
205
|
+
AtlasRb::Mash.new(write_resource(
|
|
206
|
+
multipart(nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id, payload)
|
|
183
207
|
))
|
|
184
208
|
end
|
|
185
209
|
|
|
@@ -200,12 +224,16 @@ module AtlasRb
|
|
|
200
224
|
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
201
225
|
# omitted.
|
|
202
226
|
# @return [Hash] the parsed JSON response.
|
|
227
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
228
|
+
# resource, so the write did not happen.
|
|
229
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
230
|
+
# and body.
|
|
203
231
|
#
|
|
204
232
|
# @example
|
|
205
233
|
# AtlasRb::Community.metadata("c-123", title: "New Name")
|
|
206
234
|
def self.metadata(id, values, nuid: nil, on_behalf_of: nil)
|
|
207
|
-
AtlasRb::Mash.new(
|
|
208
|
-
connection({ metadata: values }, nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id)
|
|
235
|
+
AtlasRb::Mash.new(write_resource(
|
|
236
|
+
connection({ metadata: values }, nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id)
|
|
209
237
|
))
|
|
210
238
|
end
|
|
211
239
|
|
|
@@ -227,6 +255,10 @@ module AtlasRb
|
|
|
227
255
|
# @raise [AtlasRb::StaleResourceError] if Atlas reports an optimistic-lock
|
|
228
256
|
# conflict that exhausted its internal retry budget (HTTP 409 with
|
|
229
257
|
# `error: "stale_resource"`).
|
|
258
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
259
|
+
# resource, so the write did not happen.
|
|
260
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
261
|
+
# and body.
|
|
230
262
|
#
|
|
231
263
|
# @example
|
|
232
264
|
# AtlasRb::Community.set_thumbnails(
|
|
@@ -237,9 +269,9 @@ module AtlasRb
|
|
|
237
269
|
# )
|
|
238
270
|
def self.set_thumbnails(id, thumbnail: nil, thumbnail_2x: nil, preview: nil, nuid: nil, on_behalf_of: nil)
|
|
239
271
|
body = { thumbnail: thumbnail, thumbnail_2x: thumbnail_2x, preview: preview }.compact
|
|
240
|
-
AtlasRb::Mash.new(
|
|
272
|
+
AtlasRb::Mash.new(write_resource(
|
|
241
273
|
connection({}, nuid, on_behalf_of: on_behalf_of)
|
|
242
|
-
.patch(ROUTE + id + '/thumbnails', JSON.dump(body))
|
|
274
|
+
.patch(ROUTE + id + '/thumbnails', JSON.dump(body))
|
|
243
275
|
))
|
|
244
276
|
end
|
|
245
277
|
|
data/lib/atlas_rb/compilation.rb
CHANGED
|
@@ -136,6 +136,10 @@ module AtlasRb
|
|
|
136
136
|
# e.g. a blank title).
|
|
137
137
|
# @raise [AtlasRb::ForbiddenError] if the caller may not create Sets
|
|
138
138
|
# (guests cannot).
|
|
139
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
140
|
+
# resource, so the write did not happen.
|
|
141
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
142
|
+
# and body.
|
|
139
143
|
#
|
|
140
144
|
# @example
|
|
141
145
|
# AtlasRb::Compilation.create("Course readings",
|
|
@@ -144,8 +148,8 @@ module AtlasRb
|
|
|
144
148
|
def self.create(title, description: nil, nuid: nil, on_behalf_of: nil)
|
|
145
149
|
params = { title: title }
|
|
146
150
|
params[:description] = description if description
|
|
147
|
-
AtlasRb::Mash.new(
|
|
148
|
-
connection(params, nuid, on_behalf_of: on_behalf_of).post(ROUTE)
|
|
151
|
+
AtlasRb::Mash.new(write_resource(
|
|
152
|
+
connection(params, nuid, on_behalf_of: on_behalf_of).post(ROUTE)
|
|
149
153
|
))["compilation"]
|
|
150
154
|
end
|
|
151
155
|
|
|
@@ -172,6 +176,10 @@ module AtlasRb
|
|
|
172
176
|
# @raise [AtlasRb::CompilationError] if Atlas rejects the update (422).
|
|
173
177
|
# @raise [AtlasRb::ForbiddenError] if the caller lacks edit rights
|
|
174
178
|
# (owner / explicit grant / admin).
|
|
179
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
180
|
+
# resource, so the write did not happen.
|
|
181
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
182
|
+
# and body.
|
|
175
183
|
#
|
|
176
184
|
# @example Rename
|
|
177
185
|
# AtlasRb::Compilation.update("c-123", title: "Renamed", nuid: "000000002")
|
|
@@ -185,8 +193,8 @@ module AtlasRb
|
|
|
185
193
|
params[:title] = title if title
|
|
186
194
|
params[:description] = description if description
|
|
187
195
|
params[:permissions] = permissions if permissions
|
|
188
|
-
AtlasRb::Mash.new(
|
|
189
|
-
connection(params, nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id)
|
|
196
|
+
AtlasRb::Mash.new(write_resource(
|
|
197
|
+
connection(params, nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id)
|
|
190
198
|
))["compilation"]
|
|
191
199
|
end
|
|
192
200
|
|
|
@@ -231,13 +239,17 @@ module AtlasRb
|
|
|
231
239
|
# the full recipe, so chip counts refresh without a follow-up {.find}.
|
|
232
240
|
# @raise [AtlasRb::CompilationError] if the noid is not a Collection (422).
|
|
233
241
|
# @raise [AtlasRb::ForbiddenError] if the caller lacks edit rights.
|
|
242
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
243
|
+
# resource, so the write did not happen.
|
|
244
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
245
|
+
# and body.
|
|
234
246
|
#
|
|
235
247
|
# @example
|
|
236
248
|
# AtlasRb::Compilation.add_included_collection("c-123", "col-456", nuid: "000000002")
|
|
237
249
|
def self.add_included_collection(id, collection_id, nuid: nil, on_behalf_of: nil)
|
|
238
|
-
AtlasRb::Mash.new(
|
|
250
|
+
AtlasRb::Mash.new(write_resource(
|
|
239
251
|
connection({ collection_id: collection_id }, nuid, on_behalf_of: on_behalf_of)
|
|
240
|
-
.post(ROUTE + id + '/included_collections')
|
|
252
|
+
.post(ROUTE + id + '/included_collections')
|
|
241
253
|
))["compilation"]
|
|
242
254
|
end
|
|
243
255
|
|
|
@@ -256,13 +268,17 @@ module AtlasRb
|
|
|
256
268
|
# omitted.
|
|
257
269
|
# @return [Hash] the updated `"compilation"` object.
|
|
258
270
|
# @raise [AtlasRb::ForbiddenError] if the caller lacks edit rights.
|
|
271
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
272
|
+
# resource, so the write did not happen.
|
|
273
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
274
|
+
# and body.
|
|
259
275
|
#
|
|
260
276
|
# @example
|
|
261
277
|
# AtlasRb::Compilation.remove_included_collection("c-123", "col-456", nuid: "000000002")
|
|
262
278
|
def self.remove_included_collection(id, collection_id, nuid: nil, on_behalf_of: nil)
|
|
263
|
-
AtlasRb::Mash.new(
|
|
279
|
+
AtlasRb::Mash.new(write_resource(
|
|
264
280
|
connection({}, nuid, on_behalf_of: on_behalf_of)
|
|
265
|
-
.delete(ROUTE + id + '/included_collections/' + collection_id)
|
|
281
|
+
.delete(ROUTE + id + '/included_collections/' + collection_id)
|
|
266
282
|
))["compilation"]
|
|
267
283
|
end
|
|
268
284
|
|
|
@@ -281,13 +297,17 @@ module AtlasRb
|
|
|
281
297
|
# @return [Hash] the updated `"compilation"` object.
|
|
282
298
|
# @raise [AtlasRb::CompilationError] if the noid is not a Work (422).
|
|
283
299
|
# @raise [AtlasRb::ForbiddenError] if the caller lacks edit rights.
|
|
300
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
301
|
+
# resource, so the write did not happen.
|
|
302
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
303
|
+
# and body.
|
|
284
304
|
#
|
|
285
305
|
# @example
|
|
286
306
|
# AtlasRb::Compilation.add_included_work("c-123", "w-789", nuid: "000000002")
|
|
287
307
|
def self.add_included_work(id, work_id, nuid: nil, on_behalf_of: nil)
|
|
288
|
-
AtlasRb::Mash.new(
|
|
308
|
+
AtlasRb::Mash.new(write_resource(
|
|
289
309
|
connection({ work_id: work_id }, nuid, on_behalf_of: on_behalf_of)
|
|
290
|
-
.post(ROUTE + id + '/included_works')
|
|
310
|
+
.post(ROUTE + id + '/included_works')
|
|
291
311
|
))["compilation"]
|
|
292
312
|
end
|
|
293
313
|
|
|
@@ -303,13 +323,17 @@ module AtlasRb
|
|
|
303
323
|
# omitted.
|
|
304
324
|
# @return [Hash] the updated `"compilation"` object.
|
|
305
325
|
# @raise [AtlasRb::ForbiddenError] if the caller lacks edit rights.
|
|
326
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
327
|
+
# resource, so the write did not happen.
|
|
328
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
329
|
+
# and body.
|
|
306
330
|
#
|
|
307
331
|
# @example
|
|
308
332
|
# AtlasRb::Compilation.remove_included_work("c-123", "w-789", nuid: "000000002")
|
|
309
333
|
def self.remove_included_work(id, work_id, nuid: nil, on_behalf_of: nil)
|
|
310
|
-
AtlasRb::Mash.new(
|
|
334
|
+
AtlasRb::Mash.new(write_resource(
|
|
311
335
|
connection({}, nuid, on_behalf_of: on_behalf_of)
|
|
312
|
-
.delete(ROUTE + id + '/included_works/' + work_id)
|
|
336
|
+
.delete(ROUTE + id + '/included_works/' + work_id)
|
|
313
337
|
))["compilation"]
|
|
314
338
|
end
|
|
315
339
|
|
|
@@ -331,13 +355,17 @@ module AtlasRb
|
|
|
331
355
|
# @return [Hash] the updated `"compilation"` object.
|
|
332
356
|
# @raise [AtlasRb::CompilationError] if the noid is not a Work (422).
|
|
333
357
|
# @raise [AtlasRb::ForbiddenError] if the caller lacks edit rights.
|
|
358
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
359
|
+
# resource, so the write did not happen.
|
|
360
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
361
|
+
# and body.
|
|
334
362
|
#
|
|
335
363
|
# @example
|
|
336
364
|
# AtlasRb::Compilation.add_exclusion("c-123", "w-789", nuid: "000000002")
|
|
337
365
|
def self.add_exclusion(id, work_id, nuid: nil, on_behalf_of: nil)
|
|
338
|
-
AtlasRb::Mash.new(
|
|
366
|
+
AtlasRb::Mash.new(write_resource(
|
|
339
367
|
connection({ work_id: work_id }, nuid, on_behalf_of: on_behalf_of)
|
|
340
|
-
.post(ROUTE + id + '/exclusions')
|
|
368
|
+
.post(ROUTE + id + '/exclusions')
|
|
341
369
|
))["compilation"]
|
|
342
370
|
end
|
|
343
371
|
|
|
@@ -353,13 +381,17 @@ module AtlasRb
|
|
|
353
381
|
# omitted.
|
|
354
382
|
# @return [Hash] the updated `"compilation"` object.
|
|
355
383
|
# @raise [AtlasRb::ForbiddenError] if the caller lacks edit rights.
|
|
384
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
385
|
+
# resource, so the write did not happen.
|
|
386
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
387
|
+
# and body.
|
|
356
388
|
#
|
|
357
389
|
# @example
|
|
358
390
|
# AtlasRb::Compilation.remove_exclusion("c-123", "w-789", nuid: "000000002")
|
|
359
391
|
def self.remove_exclusion(id, work_id, nuid: nil, on_behalf_of: nil)
|
|
360
|
-
AtlasRb::Mash.new(
|
|
392
|
+
AtlasRb::Mash.new(write_resource(
|
|
361
393
|
connection({}, nuid, on_behalf_of: on_behalf_of)
|
|
362
|
-
.delete(ROUTE + id + '/exclusions/' + work_id)
|
|
394
|
+
.delete(ROUTE + id + '/exclusions/' + work_id)
|
|
363
395
|
))["compilation"]
|
|
364
396
|
end
|
|
365
397
|
|