atlas_rb 1.17.0 → 1.18.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 +4 -4
- data/.version +1 -1
- data/CHANGELOG.md +64 -0
- data/Gemfile.lock +1 -1
- data/README.md +65 -12
- data/lib/atlas_rb/admin/resource.rb +69 -0
- data/lib/atlas_rb/admin.rb +5 -4
- data/lib/atlas_rb/collection.rb +5 -165
- data/lib/atlas_rb/community.rb +3 -170
- data/lib/atlas_rb/resource_writes.rb +160 -0
- data/lib/atlas_rb/work.rb +3 -171
- data/lib/atlas_rb.rb +3 -3
- metadata +3 -4
- data/lib/atlas_rb/admin/collection.rb +0 -62
- data/lib/atlas_rb/admin/community.rb +0 -62
- data/lib/atlas_rb/admin/work.rb +0 -71
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dfa719c8d880ec4ed326597fe0882537649b69b6e0e52a3df4123353c1019198
|
|
4
|
+
data.tar.gz: 1ffae14221914997c13572495b728dbb93cd02c81144cba4eb5ca33f64bb444a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b4705dd193fec70bb702846a51aa08a902a772a76964873d89a11ef2963823e7b5217525a3fb2efed339596ce213f04d6aad3cd43bb8b9387c92ea246e1d4740
|
|
7
|
+
data.tar.gz: 3ab173992d0243e249f51dd385cb6b7507bd8478adac2abcb1077e49c04bc0dfcb2d4fc665b9e843e72b17337dd9cdf8e8709008078dc0909befd87ef5472ac1
|
data/.version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.18.0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,69 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.18.0
|
|
4
|
+
|
|
5
|
+
### Changed — the write surface is type-agnostic, and the typed writes are gone
|
|
6
|
+
|
|
7
|
+
Atlas moved every write that needs no type onto the `/resources/{id}`
|
|
8
|
+
sub-resource that already served its `GET`, so the gem follows. Seven methods
|
|
9
|
+
replace twenty-one:
|
|
10
|
+
|
|
11
|
+
| Was | Is |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `Work.update` / `Collection.update` / `Community.update` | `Resource.put_mods` |
|
|
14
|
+
| `.metadata` ×3 | `Resource.set_permissions` |
|
|
15
|
+
| `.set_thumbnails` ×3 | `Resource.set_thumbnails` |
|
|
16
|
+
| `.reparent` ×3 | `Resource.reparent` |
|
|
17
|
+
| `.tombstone` ×3 | `Resource.tombstone` |
|
|
18
|
+
| `Admin::{Work,Collection,Community}.restore` | `Admin::Resource.restore` |
|
|
19
|
+
| `Admin::{Work,Collection,Community}.destroy` | `Admin::Resource.destroy` |
|
|
20
|
+
|
|
21
|
+
`AtlasRb::Admin::Work`, `::Collection` and `::Community` are **removed** —
|
|
22
|
+
purge and restore were all they held.
|
|
23
|
+
|
|
24
|
+
**Why not keep the typed names as delegators.** One URL now serves every type,
|
|
25
|
+
so `Work.set_permissions(collection_id)` would succeed: the class would name a
|
|
26
|
+
type it could not enforce. A method that cannot keep its own promise is worse
|
|
27
|
+
than no method. Note that the subclasses still *answer* the generic writes,
|
|
28
|
+
because they inherit them from `Resource` — that was already true of the
|
|
29
|
+
generic reads (`history`, `permissions`, `find_many`), and `Resource.find`
|
|
30
|
+
remains the call that reports a type.
|
|
31
|
+
|
|
32
|
+
**`update` and `metadata` are not carried forward under any name.** Neither
|
|
33
|
+
said which document it wrote: `update` was the MODS upload and `metadata` was
|
|
34
|
+
the ACL. `Resource.mods` and `Resource.permissions` were already taken by the
|
|
35
|
+
reads, so the writes take `put_mods` and `set_permissions` rather than
|
|
36
|
+
overloading a name by arity on the ACL surface.
|
|
37
|
+
|
|
38
|
+
### Changed — a generic write returns the resource unwrapped
|
|
39
|
+
|
|
40
|
+
Atlas answers a write with the resource under its type key. A caller of a
|
|
41
|
+
type-agnostic write does not know that key, so the gem unwraps it: `put_mods`,
|
|
42
|
+
`set_permissions`, `set_thumbnails` and `reparent` return the resource itself.
|
|
43
|
+
|
|
44
|
+
That also settles an inconsistency the typed methods carried — `reparent`
|
|
45
|
+
unwrapped, `update` and `metadata` did not.
|
|
46
|
+
|
|
47
|
+
`tombstone`, `Admin::Resource.restore` and `Admin::Resource.destroy` still
|
|
48
|
+
return the raw `Faraday::Response`. Atlas answers a refused tombstone with
|
|
49
|
+
`422 has_live_children`, which is a legitimate answer the caller reads rather
|
|
50
|
+
than an error to raise on.
|
|
51
|
+
|
|
52
|
+
### Changed — `Collection.set_featured` has its own path
|
|
53
|
+
|
|
54
|
+
`PATCH /collections/{id}` had a **third** payload shape beside MODS and the
|
|
55
|
+
ACL: the showcase `featured` flag. It is Collection-only, so it keeps its type
|
|
56
|
+
and moves to `PATCH /collections/{id}/featured`. The binding signature is
|
|
57
|
+
unchanged.
|
|
58
|
+
|
|
59
|
+
### Kept per-type, on purpose
|
|
60
|
+
|
|
61
|
+
`create` (it must name what to create), the Work-only writes (`complete`,
|
|
62
|
+
`mark_incomplete`, `clear_incomplete`, `set_image_derivatives`,
|
|
63
|
+
`set_derivative_permissions`, `set_full_text`, the linked-member and
|
|
64
|
+
association pairs), `Collection.set_featured`, and every FileSet, Blob and
|
|
65
|
+
Compilation write. Each addresses something only its own type has.
|
|
66
|
+
|
|
3
67
|
## 1.17.0
|
|
4
68
|
|
|
5
69
|
### Fixed — `Resource.find` emitted a type string this namespace cannot resolve
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -277,18 +277,18 @@ radius and the kind of authentication they need:
|
|
|
277
277
|
|
|
278
278
|
| Namespace | What it does | Auth | Friction |
|
|
279
279
|
|----------------------|------------------------------------------------------------------------------------|-----------------------------------------------------|---------------------------------------|
|
|
280
|
-
| `AtlasRb::*` | Regular CRUD
|
|
281
|
-
| `AtlasRb::Admin::*` | Hard delete (`destroy`) and un-tombstone (`restore`)
|
|
280
|
+
| `AtlasRb::*` | Regular CRUD — typed reads and `create`, plus every type-agnostic write on `Resource`. | Relay-signing (signed assertion, `sub` = acting NUID) | None — these are the daily-use paths. |
|
|
281
|
+
| `AtlasRb::Admin::*` | Hard delete (`destroy`) and un-tombstone (`restore`), on `Admin::Resource`. | Same as regular — a real operator is acting. | `destroy` requires `confirm: :i_understand`. |
|
|
282
282
|
| `AtlasRb::System::*` | System-context provisioning (currently just SSO user find-or-create). | System token (`Rails.application.credentials.atlas_system_token`) + `User: NUID 000000000`. | The namespace itself is the marker — there is no way to call these as a non-system principal. |
|
|
283
283
|
|
|
284
284
|
```ruby
|
|
285
285
|
# Regular daily use — picks up Current.nuid via the configured default:
|
|
286
|
-
AtlasRb::Work.find("w-789")
|
|
287
|
-
AtlasRb::
|
|
286
|
+
AtlasRb::Work.find("w-789") # typed read
|
|
287
|
+
AtlasRb::Resource.tombstone("w-789") # withdrawal (reversible), any type
|
|
288
288
|
|
|
289
289
|
# Operator-only, with a friction marker:
|
|
290
|
-
AtlasRb::Admin::
|
|
291
|
-
AtlasRb::Admin::
|
|
290
|
+
AtlasRb::Admin::Resource.destroy("w-789", confirm: :i_understand)
|
|
291
|
+
AtlasRb::Admin::Resource.restore("w-789")
|
|
292
292
|
|
|
293
293
|
# System-only — authenticates as Atlas's :system fixture:
|
|
294
294
|
AtlasRb::System::User.find_or_create(
|
|
@@ -531,6 +531,59 @@ The two mutations raise the same way `reparent` does — `LinkedMemberError`
|
|
|
531
531
|
on a structural `422` (carrying the envelope's `error` code as `#code`) and
|
|
532
532
|
`ForbiddenError` on a `403` — instead of swallowing the envelope.
|
|
533
533
|
|
|
534
|
+
### Writing without knowing the type
|
|
535
|
+
|
|
536
|
+
Atlas serves the reads as sub-resources of `/resources/{id}` and serves the
|
|
537
|
+
writes as verbs on those same paths, so a caller holding a NOID and no type can
|
|
538
|
+
write. The whole surface:
|
|
539
|
+
|
|
540
|
+
| Call | Endpoint |
|
|
541
|
+
|---|---|
|
|
542
|
+
| `Resource.put_mods(id, xml_path, origin:)` | `PUT /resources/{id}/mods` |
|
|
543
|
+
| `Resource.set_permissions(id, acl)` | `PATCH /resources/{id}/permissions` |
|
|
544
|
+
| `Resource.set_thumbnails(id, thumbnail:, thumbnail_2x:, preview:)` | `PATCH /resources/{id}/thumbnails` |
|
|
545
|
+
| `Resource.reparent(id, parent_id)` | `PATCH /resources/{id}/parent` |
|
|
546
|
+
| `Resource.tombstone(id)` | `POST /resources/{id}/tombstone` |
|
|
547
|
+
| `Admin::Resource.restore(id)` | `POST /resources/{id}/restore` |
|
|
548
|
+
| `Admin::Resource.destroy(id, confirm: :i_understand)` | `DELETE /resources/{id}` |
|
|
549
|
+
|
|
550
|
+
```ruby
|
|
551
|
+
AtlasRb::Resource.put_mods("xsj3xmz", "/tmp/work.xml", origin: "xml_editor")
|
|
552
|
+
AtlasRb::Resource.set_permissions("xsj3xmz", { "read" => ["public"] })
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
**There are no typed counterparts.** One URL serves every type, so a typed
|
|
556
|
+
write would name a type it could not enforce — `Work.set_permissions` given a
|
|
557
|
+
Collection id would succeed. The subclasses still *answer* these methods,
|
|
558
|
+
because they inherit them from `Resource`, but calling one on a subclass is the
|
|
559
|
+
same call and checks nothing. That was already true of the generic reads;
|
|
560
|
+
`Resource.find` is what reports a type.
|
|
561
|
+
|
|
562
|
+
**The ACL merges per key.** A key you omit keeps its stored value, and a key
|
|
563
|
+
sent explicitly empty is cleared. So changing one slot no longer means reading
|
|
564
|
+
the whole envelope and writing it back to avoid erasing a field you never meant
|
|
565
|
+
to touch.
|
|
566
|
+
|
|
567
|
+
```ruby
|
|
568
|
+
# publishes, and leaves the embargo, edit groups and depositor alone
|
|
569
|
+
AtlasRb::Resource.set_permissions(id, { "read" => ["public"] })
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
**MODS is a PUT because the caller assembles the whole document** — descriptive
|
|
573
|
+
merge logic lives in the client, not in Atlas. A type that holds no MODS
|
|
574
|
+
answers `404`, exactly as the `GET` on that path does; the gem does not
|
|
575
|
+
pre-check, so Atlas stays the one enforcer.
|
|
576
|
+
|
|
577
|
+
A write returns the resource **unwrapped** from its type key, since the caller
|
|
578
|
+
of a type-agnostic write has no key to index by. `tombstone`, `restore` and
|
|
579
|
+
`destroy` return the raw `Faraday::Response`: Atlas answers a refused tombstone
|
|
580
|
+
with `422 has_live_children`, which is an answer to read rather than an error
|
|
581
|
+
to raise on.
|
|
582
|
+
|
|
583
|
+
Writes that address something only one type has stay typed — `create`, the
|
|
584
|
+
Work-only lifecycle and derivative writes, `Collection.set_featured`, and the
|
|
585
|
+
FileSet, Blob and Compilation writes.
|
|
586
|
+
|
|
534
587
|
### Resolving one id of unknown type (`Resource.find` and `Resource.class_for`)
|
|
535
588
|
|
|
536
589
|
When a NOID arrives as runtime data and you do not know its type, resolve
|
|
@@ -637,16 +690,16 @@ written to prevent:
|
|
|
637
690
|
|
|
638
691
|
```ruby
|
|
639
692
|
begin
|
|
640
|
-
AtlasRb::
|
|
693
|
+
AtlasRb::Resource.put_mods("doesnotexist", "/tmp/mods.xml")
|
|
641
694
|
rescue AtlasRb::NotFoundError => e
|
|
642
695
|
e.status # => 404
|
|
643
|
-
e.message # => "
|
|
696
|
+
e.message # => "PUT /resources/doesnotexist/mods → 404 (no such resource)"
|
|
644
697
|
end
|
|
645
698
|
```
|
|
646
699
|
|
|
647
|
-
This covers every `create`
|
|
648
|
-
|
|
649
|
-
|
|
700
|
+
This covers every write: `create` and the type-specific writes on `Work`,
|
|
701
|
+
`Collection`, `Blob`, `FileSet`, `Compilation` and `Person`, and the
|
|
702
|
+
type-agnostic ones on `Resource`. Any other non-2xx a write gets raises
|
|
650
703
|
`AtlasRb::ResourceError`, which `NotFoundError` subclasses — so a caller that
|
|
651
704
|
only wants "the write failed" rescues the parent.
|
|
652
705
|
|
|
@@ -668,7 +721,7 @@ Refused writes raise on **every** binding and **every** path:
|
|
|
668
721
|
|
|
669
722
|
```ruby
|
|
670
723
|
begin
|
|
671
|
-
AtlasRb::
|
|
724
|
+
AtlasRb::Resource.set_permissions("w-789", { "read" => ["public"] })
|
|
672
725
|
rescue AtlasRb::ReadOnlyModeError => e
|
|
673
726
|
e.retry_after # => 900 (seconds, from Atlas's Retry-After header)
|
|
674
727
|
e.message # => "Atlas is in maintenance mode; writes are refused"
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AtlasRb
|
|
4
|
+
module Admin
|
|
5
|
+
# Destructive lifecycle operations that need no type, against Atlas's
|
|
6
|
+
# generic `/resources/{id}` surface.
|
|
7
|
+
#
|
|
8
|
+
# The typed {AtlasRb::Admin::Work}, {AtlasRb::Admin::Collection} and
|
|
9
|
+
# {AtlasRb::Admin::Community} delegate here. See {AtlasRb::Admin} for why
|
|
10
|
+
# the namespace exists and what `confirm: :i_understand` is for — both
|
|
11
|
+
# apply unchanged, which is why purge did not move onto
|
|
12
|
+
# {AtlasRb::Resource} beside the other generic writes.
|
|
13
|
+
class Resource
|
|
14
|
+
extend AtlasRb::FaradayHelper
|
|
15
|
+
|
|
16
|
+
# Atlas REST endpoint prefix.
|
|
17
|
+
# @api private
|
|
18
|
+
ROUTE = "/resources/"
|
|
19
|
+
|
|
20
|
+
# Hard-delete a resource — a purge, not a withdrawal.
|
|
21
|
+
#
|
|
22
|
+
# Removes the resource's metadata, cascades into its members, and removes
|
|
23
|
+
# the OCFL objects holding the preserved bytes: every retained revision,
|
|
24
|
+
# not only the current one. Nothing survives but the audit row, which
|
|
25
|
+
# records the NOIDs it removed.
|
|
26
|
+
#
|
|
27
|
+
# Unrecoverable — prefer {AtlasRb::Resource.tombstone} for the
|
|
28
|
+
# user-visible withdrawal path, which keeps everything and can be
|
|
29
|
+
# reversed. Admin-only.
|
|
30
|
+
#
|
|
31
|
+
# @param id [String] the resource's NOID.
|
|
32
|
+
# @param confirm [Symbol] must be `:i_understand`.
|
|
33
|
+
# @param nuid [String, nil] optional acting user's NUID.
|
|
34
|
+
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
35
|
+
# header.
|
|
36
|
+
# @return [Faraday::Response] the raw delete response.
|
|
37
|
+
# @raise [ArgumentError] if `confirm:` is missing or not the sentinel
|
|
38
|
+
# value. Raised before any request goes out.
|
|
39
|
+
#
|
|
40
|
+
# @example
|
|
41
|
+
# AtlasRb::Admin::Resource.destroy("xsj3xmz", confirm: :i_understand)
|
|
42
|
+
def self.destroy(id, confirm:, nuid: nil, on_behalf_of: nil)
|
|
43
|
+
unless confirm == :i_understand
|
|
44
|
+
raise ArgumentError,
|
|
45
|
+
"AtlasRb::Admin::Resource.destroy requires confirm: :i_understand"
|
|
46
|
+
end
|
|
47
|
+
connection({}, nuid, on_behalf_of: on_behalf_of).delete(ROUTE + id)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Restore a previously-tombstoned resource.
|
|
51
|
+
#
|
|
52
|
+
# Reverses a withdrawal: search and show pages stop returning a withdrawn
|
|
53
|
+
# stub. No `confirm:` marker — restoring is itself reversible, by
|
|
54
|
+
# tombstoning again.
|
|
55
|
+
#
|
|
56
|
+
# @param id [String] the resource's NOID.
|
|
57
|
+
# @param nuid [String, nil] optional acting user's NUID.
|
|
58
|
+
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
59
|
+
# header.
|
|
60
|
+
# @return [Faraday::Response] the raw response.
|
|
61
|
+
#
|
|
62
|
+
# @example
|
|
63
|
+
# AtlasRb::Admin::Resource.restore("xsj3xmz")
|
|
64
|
+
def self.restore(id, nuid: nil, on_behalf_of: nil)
|
|
65
|
+
connection({}, nuid, on_behalf_of: on_behalf_of).post(ROUTE + id + '/restore')
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
data/lib/atlas_rb/admin.rb
CHANGED
|
@@ -10,10 +10,11 @@ module AtlasRb
|
|
|
10
10
|
#
|
|
11
11
|
# ## Why a separate namespace
|
|
12
12
|
#
|
|
13
|
-
# The
|
|
14
|
-
# is structurally distinct from
|
|
15
|
-
#
|
|
16
|
-
#
|
|
13
|
+
# The namespace itself is the marker: `AtlasRb::Admin::Resource.destroy(...)`
|
|
14
|
+
# is structurally distinct from every other write. Mass-edits and code-search
|
|
15
|
+
# across a consumer codebase can find every destructive call site by grepping
|
|
16
|
+
# `AtlasRb::Admin::`. That is also why purge did not move onto
|
|
17
|
+
# {AtlasRb::Resource} beside the other type-agnostic writes.
|
|
17
18
|
#
|
|
18
19
|
# ## `confirm: :i_understand`
|
|
19
20
|
#
|
data/lib/atlas_rb/collection.rb
CHANGED
|
@@ -84,7 +84,9 @@ module AtlasRb
|
|
|
84
84
|
))["collection"]
|
|
85
85
|
return result if xml_path.to_s.empty?
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
# The MODS seed is a second call: create takes the parent and the
|
|
88
|
+
# provenance slots, and the document goes through the write that owns it.
|
|
89
|
+
AtlasRb::Resource.put_mods(result["id"], xml_path, nuid: nuid, on_behalf_of: on_behalf_of)
|
|
88
90
|
find(result["id"], nuid: nuid, on_behalf_of: on_behalf_of)
|
|
89
91
|
end
|
|
90
92
|
|
|
@@ -109,75 +111,11 @@ module AtlasRb
|
|
|
109
111
|
# AtlasRb::Collection.set_featured("col-456", true)
|
|
110
112
|
def self.set_featured(id, featured, nuid: nil, on_behalf_of: nil)
|
|
111
113
|
AtlasRb::Mash.new(write_resource(
|
|
112
|
-
connection({ featured: featured }, nuid, on_behalf_of: on_behalf_of)
|
|
114
|
+
connection({ featured: featured }, nuid, on_behalf_of: on_behalf_of)
|
|
115
|
+
.patch(ROUTE + id + '/featured')
|
|
113
116
|
))["collection"]
|
|
114
117
|
end
|
|
115
118
|
|
|
116
|
-
# Move a Collection to a different parent (Community or Collection).
|
|
117
|
-
#
|
|
118
|
-
# Wraps `PATCH /collections/<id>/parent` with a `parent_id` of the new
|
|
119
|
-
# parent. Atlas re-parents the Collection and synchronously cascades
|
|
120
|
-
# the ancestry index over its Works; the structural rules (type, cycle,
|
|
121
|
-
# tombstone guards) are enforced server-side and surface as a `422`.
|
|
122
|
-
#
|
|
123
|
-
# Mirrors {.create}'s "single parent id" shape — same kwarg threading,
|
|
124
|
-
# the only difference is the verb and that the Collection already exists.
|
|
125
|
-
#
|
|
126
|
-
# @param id [String] the Collection ID to move.
|
|
127
|
-
# @param new_parent_id [String] the destination Community or Collection ID.
|
|
128
|
-
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
|
129
|
-
# path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
|
|
130
|
-
# path it is ignored (identity lives in the token).
|
|
131
|
-
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
132
|
-
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
133
|
-
# omitted.
|
|
134
|
-
# @return [Hash] the updated `"collection"` object, already unwrapped —
|
|
135
|
-
# the same shape {.find} returns, reflecting the new `a_member_of`.
|
|
136
|
-
# @raise [AtlasRb::StaleResourceError] if Atlas reports an optimistic-lock
|
|
137
|
-
# conflict that exhausted its internal retry budget (HTTP 409 with
|
|
138
|
-
# `error: "stale_resource"`).
|
|
139
|
-
# @raise [AtlasRb::ReparentError] if Atlas rejects the move on structural
|
|
140
|
-
# grounds (HTTP 422 — `cycle`, `invalid_parent_type`, `tombstoned_node`,
|
|
141
|
-
# `tombstoned_parent`, `parent_required`, `parent_not_found`). The
|
|
142
|
-
# envelope's `error` code is exposed as `#code`.
|
|
143
|
-
# @raise [AtlasRb::ForbiddenError] if Atlas refuses the move on
|
|
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.
|
|
149
|
-
#
|
|
150
|
-
# @example
|
|
151
|
-
# AtlasRb::Collection.reparent("col-456", "c-999")
|
|
152
|
-
def self.reparent(id, new_parent_id, nuid: nil, on_behalf_of: nil)
|
|
153
|
-
AtlasRb::Mash.new(write_resource(
|
|
154
|
-
connection({ parent_id: new_parent_id }, nuid, on_behalf_of: on_behalf_of)
|
|
155
|
-
.patch(ROUTE + id + '/parent')
|
|
156
|
-
))["collection"]
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
# Tombstone (withdraw) a Collection.
|
|
160
|
-
#
|
|
161
|
-
# The Collection remains in Atlas storage but is marked as withdrawn:
|
|
162
|
-
# search and show pages return a withdrawn stub for every user. Atlas
|
|
163
|
-
# rejects the request with `422 has_live_children` if the Collection
|
|
164
|
-
# still has live (non-tombstoned) Works.
|
|
165
|
-
#
|
|
166
|
-
# @param id [String] the Collection ID.
|
|
167
|
-
# @param nuid [String] the acting user's NUID, stamped on the resource
|
|
168
|
-
# as `tombstoned_by` for audit purposes.
|
|
169
|
-
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
170
|
-
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
171
|
-
# omitted.
|
|
172
|
-
# @return [Faraday::Response] the raw response. `200`/`204` on success;
|
|
173
|
-
# `422` with `{"code":"has_live_children"}` if the Collection is not empty.
|
|
174
|
-
#
|
|
175
|
-
# @example
|
|
176
|
-
# AtlasRb::Collection.tombstone("col-456", nuid: "000000002")
|
|
177
|
-
def self.tombstone(id, nuid: nil, on_behalf_of: nil)
|
|
178
|
-
connection({}, nuid, on_behalf_of: on_behalf_of).post(ROUTE + id + '/tombstone')
|
|
179
|
-
end
|
|
180
|
-
|
|
181
119
|
# List the Works in a Collection.
|
|
182
120
|
#
|
|
183
121
|
# The endpoint returns just the noids; resolve each through
|
|
@@ -210,104 +148,6 @@ module AtlasRb
|
|
|
210
148
|
read_body(connection({}, nuid, on_behalf_of: on_behalf_of).get(ROUTE + id + '/children'))
|
|
211
149
|
end
|
|
212
150
|
|
|
213
|
-
# Replace a Collection's metadata by uploading a MODS XML document.
|
|
214
|
-
#
|
|
215
|
-
# @param id [String] the Collection ID.
|
|
216
|
-
# @param xml_path [String] path to a MODS XML file on disk.
|
|
217
|
-
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
|
218
|
-
# path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
|
|
219
|
-
# path it is ignored (identity lives in the token).
|
|
220
|
-
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
221
|
-
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
222
|
-
# omitted.
|
|
223
|
-
# @param origin [String, nil] free-text tag naming the surface that made this
|
|
224
|
-
# edit (e.g. `"metadata_form"`, `"xml_editor"`). Atlas records it verbatim
|
|
225
|
-
# on the audit event; omit it and the event carries no origin.
|
|
226
|
-
# @return [Hash] the parsed JSON response from the patch.
|
|
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.
|
|
231
|
-
#
|
|
232
|
-
# @example
|
|
233
|
-
# AtlasRb::Collection.update("col-456", "/tmp/collection-mods.xml")
|
|
234
|
-
#
|
|
235
|
-
# @example Recording which editing surface made the change
|
|
236
|
-
# AtlasRb::Collection.update("col-456", "/tmp/collection-mods.xml", origin: "xml_editor")
|
|
237
|
-
def self.update(id, xml_path, nuid: nil, on_behalf_of: nil, origin: nil)
|
|
238
|
-
AtlasRb::Mash.new(write_resource(
|
|
239
|
-
multipart(nuid, on_behalf_of: on_behalf_of)
|
|
240
|
-
.patch(ROUTE + id, mods_upload_payload(xml_path, origin))
|
|
241
|
-
))
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
# Patch individual descriptive-metadata fields without uploading a
|
|
245
|
-
# full MODS document.
|
|
246
|
-
#
|
|
247
|
-
# Scoped to user-authored descriptive metadata only. Programmatic
|
|
248
|
-
# writes of machine-set Delegate URIs (thumbnails) have their own
|
|
249
|
-
# purpose-specific endpoint — see {.set_thumbnails}.
|
|
250
|
-
#
|
|
251
|
-
# @param id [String] the Collection ID.
|
|
252
|
-
# @param values [Hash] field-level metadata updates.
|
|
253
|
-
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
|
254
|
-
# path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
|
|
255
|
-
# path it is ignored (identity lives in the token).
|
|
256
|
-
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
257
|
-
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
258
|
-
# omitted.
|
|
259
|
-
# @return [Hash] the parsed JSON response.
|
|
260
|
-
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
261
|
-
# resource, so the write did not happen.
|
|
262
|
-
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
263
|
-
# and body.
|
|
264
|
-
#
|
|
265
|
-
# @example
|
|
266
|
-
# AtlasRb::Collection.metadata("col-456", title: "Renamed Collection")
|
|
267
|
-
def self.metadata(id, values, nuid: nil, on_behalf_of: nil)
|
|
268
|
-
AtlasRb::Mash.new(write_resource(
|
|
269
|
-
connection({ metadata: values }, nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id)
|
|
270
|
-
))
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
# Attach the three thumbnail/preview Delegate URIs to a Collection.
|
|
274
|
-
#
|
|
275
|
-
# Collection-level mirror of {Work.set_thumbnails}. Atlas dispatches
|
|
276
|
-
# each non-blank URI to its matching Delegate role
|
|
277
|
-
# (`thumbnail_image` / `thumbnail_image_2x` / `preview_image`) via
|
|
278
|
-
# `DelegateUpdater`. Missing keys are left untouched.
|
|
279
|
-
#
|
|
280
|
-
# @param id [String] the Collection ID.
|
|
281
|
-
# @param thumbnail [String, nil] IIIF URI for the ~85² thumbnail.
|
|
282
|
-
# @param thumbnail_2x [String, nil] IIIF URI for the ~170² 2x thumbnail.
|
|
283
|
-
# @param preview [String, nil] IIIF URI for the ~500w preview image.
|
|
284
|
-
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
|
285
|
-
# path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
|
|
286
|
-
# path it is ignored (identity lives in the token).
|
|
287
|
-
# @return [AtlasRb::Mash] the parsed JSON response.
|
|
288
|
-
# @raise [AtlasRb::StaleResourceError] if Atlas reports an optimistic-lock
|
|
289
|
-
# conflict that exhausted its internal retry budget (HTTP 409 with
|
|
290
|
-
# `error: "stale_resource"`).
|
|
291
|
-
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
292
|
-
# resource, so the write did not happen.
|
|
293
|
-
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
294
|
-
# and body.
|
|
295
|
-
#
|
|
296
|
-
# @example
|
|
297
|
-
# AtlasRb::Collection.set_thumbnails(
|
|
298
|
-
# "col-456",
|
|
299
|
-
# thumbnail: "https://iiif.example.edu/iiif/3/c.jp2/full/!85,85/0/default.jpg",
|
|
300
|
-
# thumbnail_2x: "https://iiif.example.edu/iiif/3/c.jp2/full/!170,170/0/default.jpg",
|
|
301
|
-
# preview: "https://iiif.example.edu/iiif/3/c.jp2/full/500,/0/default.jpg"
|
|
302
|
-
# )
|
|
303
|
-
def self.set_thumbnails(id, thumbnail: nil, thumbnail_2x: nil, preview: nil, nuid: nil, on_behalf_of: nil)
|
|
304
|
-
body = { thumbnail: thumbnail, thumbnail_2x: thumbnail_2x, preview: preview }.compact
|
|
305
|
-
AtlasRb::Mash.new(write_resource(
|
|
306
|
-
connection({}, nuid, on_behalf_of: on_behalf_of)
|
|
307
|
-
.patch(ROUTE + id + '/thumbnails', JSON.dump(body))
|
|
308
|
-
))
|
|
309
|
-
end
|
|
310
|
-
|
|
311
151
|
# Fetch the Collection's MODS representation in the requested format.
|
|
312
152
|
#
|
|
313
153
|
# @param id [String] the Collection ID.
|
data/lib/atlas_rb/community.rb
CHANGED
|
@@ -82,80 +82,12 @@ module AtlasRb
|
|
|
82
82
|
))["community"]
|
|
83
83
|
return result if xml_path.to_s.empty?
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
# The MODS seed is a second call: create takes the parent and the
|
|
86
|
+
# provenance slots, and the document goes through the write that owns it.
|
|
87
|
+
AtlasRb::Resource.put_mods(result["id"], xml_path, nuid: nuid, on_behalf_of: on_behalf_of)
|
|
86
88
|
find(result["id"], nuid: nuid, on_behalf_of: on_behalf_of)
|
|
87
89
|
end
|
|
88
90
|
|
|
89
|
-
# Move a Community to a different parent Community — or to the top of the
|
|
90
|
-
# tree.
|
|
91
|
-
#
|
|
92
|
-
# Wraps `PATCH /communities/<id>/parent` with a `parent_id` of the new
|
|
93
|
-
# parent Community. Pass `new_parent_id = nil` to promote the Community to
|
|
94
|
-
# a top-level node (no parent) — mirroring how {.create} treats a `nil`
|
|
95
|
-
# `id`; the gem omits the blank param and Atlas reads it as "move to top".
|
|
96
|
-
# Atlas re-parents the Community and synchronously cascades the ancestry
|
|
97
|
-
# index over its descendant Collections and Works; the structural rules
|
|
98
|
-
# (cycle, tombstone guards) are enforced server-side and surface as a
|
|
99
|
-
# `422`.
|
|
100
|
-
#
|
|
101
|
-
# @param id [String] the Community ID to move.
|
|
102
|
-
# @param new_parent_id [String, nil] the destination Community ID, or
|
|
103
|
-
# `nil` to move the Community to the top of the tree.
|
|
104
|
-
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
|
105
|
-
# path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
|
|
106
|
-
# path it is ignored (identity lives in the token).
|
|
107
|
-
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
108
|
-
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
109
|
-
# omitted.
|
|
110
|
-
# @return [Hash] the updated `"community"` object, already unwrapped —
|
|
111
|
-
# the same shape {.find} returns, reflecting the new `a_member_of`.
|
|
112
|
-
# @raise [AtlasRb::StaleResourceError] if Atlas reports an optimistic-lock
|
|
113
|
-
# conflict that exhausted its internal retry budget (HTTP 409 with
|
|
114
|
-
# `error: "stale_resource"`).
|
|
115
|
-
# @raise [AtlasRb::ReparentError] if Atlas rejects the move on structural
|
|
116
|
-
# grounds (HTTP 422 — `cycle`, `tombstoned_node`, `tombstoned_parent`,
|
|
117
|
-
# `parent_not_found`). The envelope's `error` code is exposed as `#code`.
|
|
118
|
-
# @raise [AtlasRb::ForbiddenError] if Atlas refuses the move on
|
|
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.
|
|
124
|
-
#
|
|
125
|
-
# @example Move under another Community
|
|
126
|
-
# AtlasRb::Community.reparent("c-123", "c-999")
|
|
127
|
-
#
|
|
128
|
-
# @example Promote to a top-level Community
|
|
129
|
-
# AtlasRb::Community.reparent("c-123", nil)
|
|
130
|
-
def self.reparent(id, new_parent_id, nuid: nil, on_behalf_of: nil)
|
|
131
|
-
AtlasRb::Mash.new(write_resource(
|
|
132
|
-
connection({ parent_id: new_parent_id }, nuid, on_behalf_of: on_behalf_of)
|
|
133
|
-
.patch(ROUTE + id + '/parent')
|
|
134
|
-
))["community"]
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
# Tombstone (withdraw) a Community.
|
|
138
|
-
#
|
|
139
|
-
# The Community remains in Atlas storage but is marked as withdrawn:
|
|
140
|
-
# search and show pages return a withdrawn stub for every user. Atlas
|
|
141
|
-
# rejects the request with `422 has_live_children` if the Community
|
|
142
|
-
# still has live (non-tombstoned) members.
|
|
143
|
-
#
|
|
144
|
-
# @param id [String] the Community ID.
|
|
145
|
-
# @param nuid [String] the acting user's NUID, stamped on the resource
|
|
146
|
-
# as `tombstoned_by` for audit purposes.
|
|
147
|
-
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
148
|
-
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
149
|
-
# omitted.
|
|
150
|
-
# @return [Faraday::Response] the raw response. `200`/`204` on success;
|
|
151
|
-
# `422` with `{"code":"has_live_children"}` if the Community is not empty.
|
|
152
|
-
#
|
|
153
|
-
# @example
|
|
154
|
-
# AtlasRb::Community.tombstone("c-123", nuid: "000000002")
|
|
155
|
-
def self.tombstone(id, nuid: nil, on_behalf_of: nil)
|
|
156
|
-
connection({}, nuid, on_behalf_of: on_behalf_of).post(ROUTE + id + '/tombstone')
|
|
157
|
-
end
|
|
158
|
-
|
|
159
91
|
# List the immediate children (sub-Communities and Collections) of a Community.
|
|
160
92
|
#
|
|
161
93
|
# The endpoint returns just the noids; resolve each through
|
|
@@ -189,105 +121,6 @@ module AtlasRb
|
|
|
189
121
|
read_body(connection({}, nuid, on_behalf_of: on_behalf_of).get(ROUTE + id + '/children'))
|
|
190
122
|
end
|
|
191
123
|
|
|
192
|
-
# Replace a Community's metadata by uploading a MODS XML document.
|
|
193
|
-
#
|
|
194
|
-
# @param id [String] the Community ID.
|
|
195
|
-
# @param xml_path [String] path to a MODS XML file on disk.
|
|
196
|
-
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
|
197
|
-
# path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
|
|
198
|
-
# path it is ignored (identity lives in the token).
|
|
199
|
-
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
200
|
-
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
201
|
-
# omitted.
|
|
202
|
-
# @param origin [String, nil] free-text tag naming the surface that made this
|
|
203
|
-
# edit (e.g. `"metadata_form"`, `"xml_editor"`). Atlas records it verbatim
|
|
204
|
-
# on the audit event; omit it and the event carries no origin.
|
|
205
|
-
# @return [Hash] the parsed JSON response from the patch.
|
|
206
|
-
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
207
|
-
# resource, so the write did not happen.
|
|
208
|
-
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
209
|
-
# and body.
|
|
210
|
-
#
|
|
211
|
-
# @example
|
|
212
|
-
# AtlasRb::Community.update("c-123", "/tmp/community-mods.xml")
|
|
213
|
-
#
|
|
214
|
-
# @example Recording which editing surface made the change
|
|
215
|
-
# AtlasRb::Community.update("c-123", "/tmp/community-mods.xml", origin: "xml_editor")
|
|
216
|
-
def self.update(id, xml_path, nuid: nil, on_behalf_of: nil, origin: nil)
|
|
217
|
-
AtlasRb::Mash.new(write_resource(
|
|
218
|
-
multipart(nuid, on_behalf_of: on_behalf_of)
|
|
219
|
-
.patch(ROUTE + id, mods_upload_payload(xml_path, origin))
|
|
220
|
-
))
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
# Patch individual descriptive-metadata fields without uploading a
|
|
224
|
-
# full MODS document.
|
|
225
|
-
#
|
|
226
|
-
# Scoped to user-authored descriptive metadata only. Programmatic
|
|
227
|
-
# writes of machine-set Delegate URIs (thumbnails) have their own
|
|
228
|
-
# purpose-specific endpoint — see {.set_thumbnails}.
|
|
229
|
-
#
|
|
230
|
-
# @param id [String] the Community ID.
|
|
231
|
-
# @param values [Hash] field-level metadata updates (shape determined by
|
|
232
|
-
# the Atlas server, typically a mapping from MODS field name to value).
|
|
233
|
-
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
|
234
|
-
# path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
|
|
235
|
-
# path it is ignored (identity lives in the token).
|
|
236
|
-
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
237
|
-
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
238
|
-
# omitted.
|
|
239
|
-
# @return [Hash] the parsed JSON response.
|
|
240
|
-
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
241
|
-
# resource, so the write did not happen.
|
|
242
|
-
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
243
|
-
# and body.
|
|
244
|
-
#
|
|
245
|
-
# @example
|
|
246
|
-
# AtlasRb::Community.metadata("c-123", title: "New Name")
|
|
247
|
-
def self.metadata(id, values, nuid: nil, on_behalf_of: nil)
|
|
248
|
-
AtlasRb::Mash.new(write_resource(
|
|
249
|
-
connection({ metadata: values }, nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id)
|
|
250
|
-
))
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
# Attach the three thumbnail/preview Delegate URIs to a Community.
|
|
254
|
-
#
|
|
255
|
-
# Community-level mirror of {Work.set_thumbnails}. Atlas dispatches
|
|
256
|
-
# each non-blank URI to its matching Delegate role
|
|
257
|
-
# (`thumbnail_image` / `thumbnail_image_2x` / `preview_image`) via
|
|
258
|
-
# `DelegateUpdater`. Missing keys are left untouched.
|
|
259
|
-
#
|
|
260
|
-
# @param id [String] the Community ID.
|
|
261
|
-
# @param thumbnail [String, nil] IIIF URI for the ~85² thumbnail.
|
|
262
|
-
# @param thumbnail_2x [String, nil] IIIF URI for the ~170² 2x thumbnail.
|
|
263
|
-
# @param preview [String, nil] IIIF URI for the ~500w preview image.
|
|
264
|
-
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
|
265
|
-
# path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
|
|
266
|
-
# path it is ignored (identity lives in the token).
|
|
267
|
-
# @return [AtlasRb::Mash] the parsed JSON response.
|
|
268
|
-
# @raise [AtlasRb::StaleResourceError] if Atlas reports an optimistic-lock
|
|
269
|
-
# conflict that exhausted its internal retry budget (HTTP 409 with
|
|
270
|
-
# `error: "stale_resource"`).
|
|
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.
|
|
275
|
-
#
|
|
276
|
-
# @example
|
|
277
|
-
# AtlasRb::Community.set_thumbnails(
|
|
278
|
-
# "c-123",
|
|
279
|
-
# thumbnail: "https://iiif.example.edu/iiif/3/m.jp2/full/!85,85/0/default.jpg",
|
|
280
|
-
# thumbnail_2x: "https://iiif.example.edu/iiif/3/m.jp2/full/!170,170/0/default.jpg",
|
|
281
|
-
# preview: "https://iiif.example.edu/iiif/3/m.jp2/full/500,/0/default.jpg"
|
|
282
|
-
# )
|
|
283
|
-
def self.set_thumbnails(id, thumbnail: nil, thumbnail_2x: nil, preview: nil, nuid: nil, on_behalf_of: nil)
|
|
284
|
-
body = { thumbnail: thumbnail, thumbnail_2x: thumbnail_2x, preview: preview }.compact
|
|
285
|
-
AtlasRb::Mash.new(write_resource(
|
|
286
|
-
connection({}, nuid, on_behalf_of: on_behalf_of)
|
|
287
|
-
.patch(ROUTE + id + '/thumbnails', JSON.dump(body))
|
|
288
|
-
))
|
|
289
|
-
end
|
|
290
|
-
|
|
291
124
|
# Fetch the Community's MODS representation in the requested format.
|
|
292
125
|
#
|
|
293
126
|
# @param id [String] the Community ID.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AtlasRb
|
|
4
|
+
# Reopens {Resource} with the writes that need no type. Atlas serves each one
|
|
5
|
+
# as a verb on the `/resources/{id}` sub-resource that already serves its
|
|
6
|
+
# `GET`, so a caller holding only a NOID never resolves the type first.
|
|
7
|
+
#
|
|
8
|
+
# Loaded after the subclasses because the typed writes delegate here, and
|
|
9
|
+
# because {Resource::TYPE_MAP} in `resource_types.rb` has the same ordering
|
|
10
|
+
# requirement.
|
|
11
|
+
#
|
|
12
|
+
# **The names are deliberately not `update` and `metadata`.** Neither typed
|
|
13
|
+
# name says which document it writes, and `Resource.mods` / `.permissions`
|
|
14
|
+
# are already taken by the reads — overloading them by arity would give one
|
|
15
|
+
# name two behaviours on the ACL surface.
|
|
16
|
+
#
|
|
17
|
+
# Atlas refuses a type that cannot take the write; the gem does not
|
|
18
|
+
# pre-check. A MODS write aimed at a FileSet answers `404`, the same as the
|
|
19
|
+
# `GET` on that path.
|
|
20
|
+
#
|
|
21
|
+
# **There are no typed counterparts.** One URL serves every type, so a typed
|
|
22
|
+
# write would name a type it could not enforce. The subclasses still *answer*
|
|
23
|
+
# these methods, because they inherit them — `AtlasRb::Work.tombstone(id)` is
|
|
24
|
+
# the same call as `AtlasRb::Resource.tombstone(id)`, and neither checks that
|
|
25
|
+
# `id` names a Work. That has always been true of the generic reads too;
|
|
26
|
+
# {Resource.find} is the call that reports a type.
|
|
27
|
+
class Resource
|
|
28
|
+
# Replace a resource's MODS document.
|
|
29
|
+
#
|
|
30
|
+
# `PUT`, not `PATCH`: the caller assembles the whole document. Descriptive
|
|
31
|
+
# merge logic lives in the client, so a partial document replaces rather
|
|
32
|
+
# than merges, and the verb says so.
|
|
33
|
+
#
|
|
34
|
+
# @param id [String] the resource's NOID.
|
|
35
|
+
# @param xml_path [String] path to the MODS XML to upload.
|
|
36
|
+
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
|
37
|
+
# path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
|
|
38
|
+
# path it is ignored (identity lives in the token).
|
|
39
|
+
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
40
|
+
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
41
|
+
# omitted.
|
|
42
|
+
# @param origin [String, nil] the editing surface to record on the audit
|
|
43
|
+
# event, e.g. `"xml_editor"`. Omitted from the body when nil.
|
|
44
|
+
# @return [AtlasRb::Mash] the resource, unwrapped from its type key.
|
|
45
|
+
# @raise [AtlasRb::NotFoundError] on `404` — no such id, or a type that
|
|
46
|
+
# holds no MODS. The write did not happen either way.
|
|
47
|
+
# @raise [AtlasRb::StaleResourceError] on an optimistic-lock conflict.
|
|
48
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx.
|
|
49
|
+
#
|
|
50
|
+
# @example
|
|
51
|
+
# AtlasRb::Resource.put_mods("xsj3xmz", "/tmp/work.xml", origin: "xml_editor")
|
|
52
|
+
def self.put_mods(id, xml_path, nuid: nil, on_behalf_of: nil, origin: nil)
|
|
53
|
+
unwrap(write_resource(
|
|
54
|
+
multipart(nuid, on_behalf_of: on_behalf_of)
|
|
55
|
+
.put('/resources/' + id + '/mods', mods_upload_payload(xml_path, origin))
|
|
56
|
+
))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Adjust a resource's ACL.
|
|
60
|
+
#
|
|
61
|
+
# `PATCH`, and every key merges: a key you omit keeps its stored value.
|
|
62
|
+
# Pass an explicit empty array to clear one. So changing a single slot no
|
|
63
|
+
# longer needs the read-the-whole-envelope-and-write-it-back round trip.
|
|
64
|
+
#
|
|
65
|
+
# @param id [String] the resource's NOID.
|
|
66
|
+
# @param values [Hash] the ACL keys to change — any of `embargo`,
|
|
67
|
+
# `depositor`, `proxy_uploader`, `edit_users`, `read`, `edit`.
|
|
68
|
+
# @param nuid [String, nil] optional acting user's NUID.
|
|
69
|
+
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
70
|
+
# header.
|
|
71
|
+
# @return [AtlasRb::Mash] the resource, unwrapped from its type key.
|
|
72
|
+
# @raise [AtlasRb::NotFoundError] on `404` — the write did not happen.
|
|
73
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, including the `403`
|
|
74
|
+
# Atlas answers when a caller tries to remove a grant for a group it does
|
|
75
|
+
# not belong to.
|
|
76
|
+
#
|
|
77
|
+
# @example Publish, leaving every other key alone
|
|
78
|
+
# AtlasRb::Resource.set_permissions("xsj3xmz", { "read" => ["public"] })
|
|
79
|
+
def self.set_permissions(id, values, nuid: nil, on_behalf_of: nil)
|
|
80
|
+
unwrap(write_resource(
|
|
81
|
+
connection({ permissions: values }, nuid, on_behalf_of: on_behalf_of)
|
|
82
|
+
.patch('/resources/' + id + '/permissions')
|
|
83
|
+
))
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Attach the three thumbnail-family IIIF Delegate URIs to a resource.
|
|
87
|
+
#
|
|
88
|
+
# Only the URIs you pass are upserted; an omitted key is left untouched.
|
|
89
|
+
#
|
|
90
|
+
# @param id [String] the resource's NOID.
|
|
91
|
+
# @param thumbnail [String, nil] IIIF URI for the ~85² thumbnail.
|
|
92
|
+
# @param thumbnail_2x [String, nil] IIIF URI for the ~170² 2x thumbnail.
|
|
93
|
+
# @param preview [String, nil] IIIF URI for the ~500w preview image.
|
|
94
|
+
# @param nuid [String, nil] optional acting user's NUID.
|
|
95
|
+
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
96
|
+
# header.
|
|
97
|
+
# @return [AtlasRb::Mash] the resource, unwrapped from its type key.
|
|
98
|
+
# @raise [AtlasRb::NotFoundError] on `404` — the write did not happen.
|
|
99
|
+
# @raise [AtlasRb::StaleResourceError] on an optimistic-lock conflict.
|
|
100
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx.
|
|
101
|
+
def self.set_thumbnails(id, thumbnail: nil, thumbnail_2x: nil, preview: nil, nuid: nil, on_behalf_of: nil)
|
|
102
|
+
body = { thumbnail: thumbnail, thumbnail_2x: thumbnail_2x, preview: preview }.compact
|
|
103
|
+
unwrap(write_resource(
|
|
104
|
+
connection({}, nuid, on_behalf_of: on_behalf_of)
|
|
105
|
+
.patch('/resources/' + id + '/thumbnails', JSON.dump(body))
|
|
106
|
+
))
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Move a resource under a different parent.
|
|
110
|
+
#
|
|
111
|
+
# Authorization is two-sided — the caller needs the right on the moved node
|
|
112
|
+
# **and** on the destination. Omit `new_parent_id` to move a Community to
|
|
113
|
+
# the top of the tree.
|
|
114
|
+
#
|
|
115
|
+
# @param id [String] the NOID of the resource to move.
|
|
116
|
+
# @param new_parent_id [String, nil] the destination's NOID.
|
|
117
|
+
# @param nuid [String, nil] optional acting user's NUID.
|
|
118
|
+
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
119
|
+
# header.
|
|
120
|
+
# @return [AtlasRb::Mash] the resource, unwrapped from its type key.
|
|
121
|
+
# @raise [AtlasRb::NotFoundError] on `404` — the write did not happen.
|
|
122
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, including the `422`
|
|
123
|
+
# Atlas answers for an unresolvable destination or a containment refusal.
|
|
124
|
+
def self.reparent(id, new_parent_id = nil, nuid: nil, on_behalf_of: nil)
|
|
125
|
+
unwrap(write_resource(
|
|
126
|
+
connection({ parent_id: new_parent_id }, nuid, on_behalf_of: on_behalf_of)
|
|
127
|
+
.patch('/resources/' + id + '/parent')
|
|
128
|
+
))
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Restore is the operator's counterpart and lives in
|
|
132
|
+
# {AtlasRb::Admin::Resource}, where the namespace is the marker.
|
|
133
|
+
#
|
|
134
|
+
# Tombstone (withdraw) a resource.
|
|
135
|
+
#
|
|
136
|
+
# Returns the **raw response** rather than raising, because Atlas refuses a
|
|
137
|
+
# container that still holds live children with a `422` carrying
|
|
138
|
+
# `has_live_children` — a legitimate answer the caller has to read, not an
|
|
139
|
+
# error. Reversible via {.restore}.
|
|
140
|
+
#
|
|
141
|
+
# @param id [String] the resource's NOID.
|
|
142
|
+
# @param nuid [String, nil] the acting user's NUID, stamped on the resource
|
|
143
|
+
# as `tombstoned_by`.
|
|
144
|
+
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
145
|
+
# header.
|
|
146
|
+
# @return [Faraday::Response] the raw response — read `status` yourself.
|
|
147
|
+
def self.tombstone(id, nuid: nil, on_behalf_of: nil)
|
|
148
|
+
connection({}, nuid, on_behalf_of: on_behalf_of).post('/resources/' + id + '/tombstone')
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Atlas answers a write with the resource under its type key, matching what
|
|
152
|
+
# `GET /{type}/{id}` returns. The caller of a type-agnostic write does not
|
|
153
|
+
# know that key, so it is unwrapped here rather than left for them to
|
|
154
|
+
# guess -- {Resource.find} is the call that reports a type.
|
|
155
|
+
def self.unwrap(body)
|
|
156
|
+
AtlasRb::Mash.new(body).values.first
|
|
157
|
+
end
|
|
158
|
+
private_class_method :unwrap
|
|
159
|
+
end
|
|
160
|
+
end
|
data/lib/atlas_rb/work.rb
CHANGED
|
@@ -147,79 +147,12 @@ module AtlasRb
|
|
|
147
147
|
))["work"]
|
|
148
148
|
return result if xml_path.to_s.empty?
|
|
149
149
|
|
|
150
|
-
|
|
150
|
+
# The MODS seed is a second call: create takes the parent and the
|
|
151
|
+
# provenance slots, and the document goes through the write that owns it.
|
|
152
|
+
AtlasRb::Resource.put_mods(result["id"], xml_path, nuid: nuid, on_behalf_of: on_behalf_of)
|
|
151
153
|
find(result["id"], nuid: nuid, on_behalf_of: on_behalf_of)
|
|
152
154
|
end
|
|
153
155
|
|
|
154
|
-
# Move a Work to a different parent Collection.
|
|
155
|
-
#
|
|
156
|
-
# Wraps `PATCH /works/<id>/parent` with a `parent_id` of the new
|
|
157
|
-
# Collection. This changes the Work's single **structural** home
|
|
158
|
-
# (`a_member_of`) — distinct from {.add_linked_member}, which adds an
|
|
159
|
-
# additional *linked* membership without moving the Work. Atlas
|
|
160
|
-
# re-parents the Work and synchronously updates its ancestry index; the
|
|
161
|
-
# structural rules (type, cycle, tombstone guards) are enforced
|
|
162
|
-
# server-side and surface as a `422`.
|
|
163
|
-
#
|
|
164
|
-
# **Note**: like {.create}, the destination here is a **Collection**, but
|
|
165
|
-
# the underlying request still uses the shared `parent_id` body key (not
|
|
166
|
-
# `collection_id`) — every re-parent endpoint posts `{ parent_id }`.
|
|
167
|
-
#
|
|
168
|
-
# @param id [String] the Work ID to move.
|
|
169
|
-
# @param new_collection_id [String] the destination Collection ID.
|
|
170
|
-
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
|
171
|
-
# path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
|
|
172
|
-
# path it is ignored (identity lives in the token).
|
|
173
|
-
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
174
|
-
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
175
|
-
# omitted.
|
|
176
|
-
# @return [Hash] the updated `"work"` object, already unwrapped — the
|
|
177
|
-
# same shape {.find} returns, reflecting the new `a_member_of`.
|
|
178
|
-
# @raise [AtlasRb::StaleResourceError] if Atlas reports an optimistic-lock
|
|
179
|
-
# conflict that exhausted its internal retry budget (HTTP 409 with
|
|
180
|
-
# `error: "stale_resource"`).
|
|
181
|
-
# @raise [AtlasRb::ReparentError] if Atlas rejects the move on structural
|
|
182
|
-
# grounds (HTTP 422 — `cycle`, `invalid_parent_type`, `tombstoned_node`,
|
|
183
|
-
# `tombstoned_parent`, `parent_required`, `parent_not_found`). The
|
|
184
|
-
# envelope's `error` code is exposed as `#code`.
|
|
185
|
-
# @raise [AtlasRb::ForbiddenError] if Atlas refuses the move on
|
|
186
|
-
# authorization grounds (HTTP 403).
|
|
187
|
-
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
188
|
-
# resource, so the write did not happen.
|
|
189
|
-
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
190
|
-
# and body.
|
|
191
|
-
#
|
|
192
|
-
# @example
|
|
193
|
-
# AtlasRb::Work.reparent("w-789", "col-999")
|
|
194
|
-
def self.reparent(id, new_collection_id, nuid: nil, on_behalf_of: nil)
|
|
195
|
-
AtlasRb::Mash.new(write_resource(
|
|
196
|
-
connection({ parent_id: new_collection_id }, nuid, on_behalf_of: on_behalf_of)
|
|
197
|
-
.patch(ROUTE + id + '/parent')
|
|
198
|
-
))["work"]
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
# Tombstone (withdraw) a Work.
|
|
202
|
-
#
|
|
203
|
-
# The Work remains in Atlas storage along with its FileSets and Blobs,
|
|
204
|
-
# but is marked as withdrawn: search and show pages return a withdrawn
|
|
205
|
-
# stub for every user. Unlike Communities and Collections, Works are
|
|
206
|
-
# always tombstoneable regardless of how many files they hold — the
|
|
207
|
-
# FileSets and Blobs ride along.
|
|
208
|
-
#
|
|
209
|
-
# @param id [String] the Work ID.
|
|
210
|
-
# @param nuid [String] the acting user's NUID, stamped on the resource
|
|
211
|
-
# as `tombstoned_by` for audit purposes.
|
|
212
|
-
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
213
|
-
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
214
|
-
# omitted.
|
|
215
|
-
# @return [Faraday::Response] the raw response.
|
|
216
|
-
#
|
|
217
|
-
# @example
|
|
218
|
-
# AtlasRb::Work.tombstone("w-789", nuid: "000000002")
|
|
219
|
-
def self.tombstone(id, nuid: nil, on_behalf_of: nil)
|
|
220
|
-
connection({}, nuid, on_behalf_of: on_behalf_of).post(ROUTE + id + '/tombstone')
|
|
221
|
-
end
|
|
222
|
-
|
|
223
156
|
# Mark a Work complete.
|
|
224
157
|
#
|
|
225
158
|
# Cerberus's bulk-deposit job calls this once it has confirmed all
|
|
@@ -348,107 +281,6 @@ module AtlasRb
|
|
|
348
281
|
))["work"]
|
|
349
282
|
end
|
|
350
283
|
|
|
351
|
-
# Replace a Work's metadata by uploading a MODS XML document.
|
|
352
|
-
#
|
|
353
|
-
# @param id [String] the Work ID.
|
|
354
|
-
# @param xml_path [String] path to a MODS XML file on disk.
|
|
355
|
-
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
|
356
|
-
# path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
|
|
357
|
-
# path it is ignored (identity lives in the token).
|
|
358
|
-
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
359
|
-
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
360
|
-
# omitted.
|
|
361
|
-
# @param origin [String, nil] free-text tag naming the surface that made this
|
|
362
|
-
# edit (e.g. `"metadata_form"`, `"xml_editor"`). Atlas records it verbatim
|
|
363
|
-
# on the audit event; omit it and the event carries no origin.
|
|
364
|
-
# @return [Hash] the parsed JSON response from the patch.
|
|
365
|
-
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
366
|
-
# resource, so the write did not happen.
|
|
367
|
-
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
368
|
-
# and body.
|
|
369
|
-
#
|
|
370
|
-
# @example
|
|
371
|
-
# AtlasRb::Work.update("w-789", "/tmp/work-mods.xml")
|
|
372
|
-
#
|
|
373
|
-
# @example Recording which editing surface made the change
|
|
374
|
-
# AtlasRb::Work.update("w-789", "/tmp/work-mods.xml", origin: "xml_editor")
|
|
375
|
-
def self.update(id, xml_path, nuid: nil, on_behalf_of: nil, origin: nil)
|
|
376
|
-
AtlasRb::Mash.new(write_resource(
|
|
377
|
-
multipart(nuid, on_behalf_of: on_behalf_of)
|
|
378
|
-
.patch(ROUTE + id, mods_upload_payload(xml_path, origin))
|
|
379
|
-
))
|
|
380
|
-
end
|
|
381
|
-
|
|
382
|
-
# Patch individual descriptive-metadata fields without uploading a
|
|
383
|
-
# full MODS document.
|
|
384
|
-
#
|
|
385
|
-
# Scoped to user-authored descriptive metadata only. Programmatic
|
|
386
|
-
# writes of machine-set Delegate URIs (thumbnails, image
|
|
387
|
-
# derivatives) have their own purpose-specific endpoints — see
|
|
388
|
-
# {.set_thumbnails} and {.set_image_derivatives}.
|
|
389
|
-
#
|
|
390
|
-
# @param id [String] the Work ID.
|
|
391
|
-
# @param values [Hash] field-level metadata updates.
|
|
392
|
-
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
|
393
|
-
# path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
|
|
394
|
-
# path it is ignored (identity lives in the token).
|
|
395
|
-
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
396
|
-
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
397
|
-
# omitted.
|
|
398
|
-
# @return [Hash] the parsed JSON response.
|
|
399
|
-
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
400
|
-
# resource, so the write did not happen.
|
|
401
|
-
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
402
|
-
# and body.
|
|
403
|
-
#
|
|
404
|
-
# @example
|
|
405
|
-
# AtlasRb::Work.metadata("w-789", title: "Revised Title")
|
|
406
|
-
def self.metadata(id, values, nuid: nil, on_behalf_of: nil)
|
|
407
|
-
AtlasRb::Mash.new(write_resource(
|
|
408
|
-
connection({ metadata: values }, nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id)
|
|
409
|
-
))
|
|
410
|
-
end
|
|
411
|
-
|
|
412
|
-
# Attach the three thumbnail/preview Delegate URIs to a Work.
|
|
413
|
-
#
|
|
414
|
-
# Purpose-specific PATCH for the `thumbnail_image` /
|
|
415
|
-
# `thumbnail_image_2x` / `preview_image` Delegate roles. Atlas
|
|
416
|
-
# dispatches each URI to its matching role via `DelegateUpdater`.
|
|
417
|
-
# Distinct from {.metadata} — these are machine-set IIIF URIs, not
|
|
418
|
-
# user-authored descriptive content. Missing keys are left
|
|
419
|
-
# untouched server-side; only the URIs you pass are upserted.
|
|
420
|
-
#
|
|
421
|
-
# @param id [String] the Work ID.
|
|
422
|
-
# @param thumbnail [String, nil] IIIF URI for the ~85² thumbnail.
|
|
423
|
-
# @param thumbnail_2x [String, nil] IIIF URI for the ~170² 2x thumbnail.
|
|
424
|
-
# @param preview [String, nil] IIIF URI for the ~500w preview image.
|
|
425
|
-
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
|
426
|
-
# path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
|
|
427
|
-
# path it is ignored (identity lives in the token).
|
|
428
|
-
# @return [AtlasRb::Mash] the parsed JSON response.
|
|
429
|
-
# @raise [AtlasRb::StaleResourceError] if Atlas reports an optimistic-lock
|
|
430
|
-
# conflict that exhausted its internal retry budget (HTTP 409 with
|
|
431
|
-
# `error: "stale_resource"`).
|
|
432
|
-
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
|
|
433
|
-
# resource, so the write did not happen.
|
|
434
|
-
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
|
|
435
|
-
# and body.
|
|
436
|
-
#
|
|
437
|
-
# @example
|
|
438
|
-
# AtlasRb::Work.set_thumbnails(
|
|
439
|
-
# "w-789",
|
|
440
|
-
# thumbnail: "https://iiif.example.edu/iiif/3/abc.jp2/full/!85,85/0/default.jpg",
|
|
441
|
-
# thumbnail_2x: "https://iiif.example.edu/iiif/3/abc.jp2/full/!170,170/0/default.jpg",
|
|
442
|
-
# preview: "https://iiif.example.edu/iiif/3/abc.jp2/full/500,/0/default.jpg"
|
|
443
|
-
# )
|
|
444
|
-
def self.set_thumbnails(id, thumbnail: nil, thumbnail_2x: nil, preview: nil, nuid: nil, on_behalf_of: nil)
|
|
445
|
-
body = { thumbnail: thumbnail, thumbnail_2x: thumbnail_2x, preview: preview }.compact
|
|
446
|
-
AtlasRb::Mash.new(write_resource(
|
|
447
|
-
connection({}, nuid, on_behalf_of: on_behalf_of)
|
|
448
|
-
.patch(ROUTE + id + '/thumbnails', JSON.dump(body))
|
|
449
|
-
))
|
|
450
|
-
end
|
|
451
|
-
|
|
452
284
|
# Attach the three image-derivative Delegate URIs to a Work.
|
|
453
285
|
#
|
|
454
286
|
# Sibling of {.set_thumbnails} for the `small_image` /
|
data/lib/atlas_rb.rb
CHANGED
|
@@ -35,11 +35,11 @@ require_relative "atlas_rb/compilation"
|
|
|
35
35
|
require_relative "atlas_rb/person"
|
|
36
36
|
# Must follow every Resource subclass above: it names them in a map.
|
|
37
37
|
require_relative "atlas_rb/resource_types"
|
|
38
|
+
# Same ordering requirement as resource_types: the typed writes delegate here.
|
|
39
|
+
require_relative "atlas_rb/resource_writes"
|
|
38
40
|
require_relative "atlas_rb/user"
|
|
39
41
|
require_relative "atlas_rb/admin"
|
|
40
|
-
require_relative "atlas_rb/admin/
|
|
41
|
-
require_relative "atlas_rb/admin/collection"
|
|
42
|
-
require_relative "atlas_rb/admin/community"
|
|
42
|
+
require_relative "atlas_rb/admin/resource"
|
|
43
43
|
require_relative "atlas_rb/system"
|
|
44
44
|
require_relative "atlas_rb/system/user"
|
|
45
45
|
require_relative "atlas_rb/system/token"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: atlas_rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.18.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Cliff
|
|
@@ -158,9 +158,7 @@ files:
|
|
|
158
158
|
- bin/setup
|
|
159
159
|
- lib/atlas_rb.rb
|
|
160
160
|
- lib/atlas_rb/admin.rb
|
|
161
|
-
- lib/atlas_rb/admin/
|
|
162
|
-
- lib/atlas_rb/admin/community.rb
|
|
163
|
-
- lib/atlas_rb/admin/work.rb
|
|
161
|
+
- lib/atlas_rb/admin/resource.rb
|
|
164
162
|
- lib/atlas_rb/audit_event.rb
|
|
165
163
|
- lib/atlas_rb/authentication.rb
|
|
166
164
|
- lib/atlas_rb/blob.rb
|
|
@@ -181,6 +179,7 @@ files:
|
|
|
181
179
|
- lib/atlas_rb/person.rb
|
|
182
180
|
- lib/atlas_rb/resource.rb
|
|
183
181
|
- lib/atlas_rb/resource_types.rb
|
|
182
|
+
- lib/atlas_rb/resource_writes.rb
|
|
184
183
|
- lib/atlas_rb/system.rb
|
|
185
184
|
- lib/atlas_rb/system/token.rb
|
|
186
185
|
- lib/atlas_rb/system/user.rb
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module AtlasRb
|
|
4
|
-
module Admin
|
|
5
|
-
# Destructive lifecycle operations on a {AtlasRb::Collection}.
|
|
6
|
-
#
|
|
7
|
-
# See {AtlasRb::Admin} for the rationale behind the namespace and the
|
|
8
|
-
# `confirm: :i_understand` friction marker.
|
|
9
|
-
class Collection
|
|
10
|
-
extend AtlasRb::FaradayHelper
|
|
11
|
-
|
|
12
|
-
# Atlas REST endpoint prefix.
|
|
13
|
-
# @api private
|
|
14
|
-
ROUTE = "/collections/"
|
|
15
|
-
|
|
16
|
-
# Hard-delete a Collection — a purge, not a withdrawal.
|
|
17
|
-
#
|
|
18
|
-
# Removes the Collection's metadata, its descriptive-metadata
|
|
19
|
-
# FileSet, and the OCFL objects holding the preserved bytes.
|
|
20
|
-
# Unrecoverable — prefer {AtlasRb::Collection.tombstone} for
|
|
21
|
-
# user-visible withdrawal. Admin-only.
|
|
22
|
-
#
|
|
23
|
-
# Atlas refuses with a `422` (`has_children`) while the Collection
|
|
24
|
-
# still holds a Work or a sub-container, **including tombstoned
|
|
25
|
-
# ones**. That is stricter than tombstone, which counts only live
|
|
26
|
-
# members: a member left behind by a purge is orphaned for good.
|
|
27
|
-
# Empty the tree leaf-first.
|
|
28
|
-
#
|
|
29
|
-
# @param id [String] the Collection ID.
|
|
30
|
-
# @param confirm [Symbol] must be `:i_understand`. Any other value
|
|
31
|
-
# raises `ArgumentError`.
|
|
32
|
-
# @param nuid [String, nil] optional acting user's NUID.
|
|
33
|
-
# @param on_behalf_of [String, nil] optional `On-Behalf-Of` NUID.
|
|
34
|
-
# @return [Faraday::Response] the raw delete response.
|
|
35
|
-
# @raise [ArgumentError] if `confirm:` is missing or not the
|
|
36
|
-
# sentinel value.
|
|
37
|
-
#
|
|
38
|
-
# @example
|
|
39
|
-
# AtlasRb::Admin::Collection.destroy("col-456", confirm: :i_understand)
|
|
40
|
-
def self.destroy(id, confirm:, nuid: nil, on_behalf_of: nil)
|
|
41
|
-
unless confirm == :i_understand
|
|
42
|
-
raise ArgumentError,
|
|
43
|
-
"AtlasRb::Admin::Collection.destroy requires confirm: :i_understand"
|
|
44
|
-
end
|
|
45
|
-
connection({}, nuid, on_behalf_of: on_behalf_of).delete(ROUTE + id)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Restore a previously-tombstoned Collection.
|
|
49
|
-
#
|
|
50
|
-
# @param id [String] the Collection ID.
|
|
51
|
-
# @param nuid [String, nil] optional acting user's NUID.
|
|
52
|
-
# @param on_behalf_of [String, nil] optional `On-Behalf-Of` NUID.
|
|
53
|
-
# @return [Faraday::Response] the raw response.
|
|
54
|
-
#
|
|
55
|
-
# @example
|
|
56
|
-
# AtlasRb::Admin::Collection.restore("col-456")
|
|
57
|
-
def self.restore(id, nuid: nil, on_behalf_of: nil)
|
|
58
|
-
connection({}, nuid, on_behalf_of: on_behalf_of).post(ROUTE + id + '/restore')
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module AtlasRb
|
|
4
|
-
module Admin
|
|
5
|
-
# Destructive lifecycle operations on a {AtlasRb::Community}.
|
|
6
|
-
#
|
|
7
|
-
# See {AtlasRb::Admin} for the rationale behind the namespace and the
|
|
8
|
-
# `confirm: :i_understand` friction marker.
|
|
9
|
-
class Community
|
|
10
|
-
extend AtlasRb::FaradayHelper
|
|
11
|
-
|
|
12
|
-
# Atlas REST endpoint prefix.
|
|
13
|
-
# @api private
|
|
14
|
-
ROUTE = "/communities/"
|
|
15
|
-
|
|
16
|
-
# Hard-delete a Community — a purge, not a withdrawal.
|
|
17
|
-
#
|
|
18
|
-
# Removes the Community's metadata, its descriptive-metadata
|
|
19
|
-
# FileSet, and the OCFL objects holding the preserved bytes.
|
|
20
|
-
# Unrecoverable — prefer {AtlasRb::Community.tombstone} for
|
|
21
|
-
# user-visible withdrawal. Admin-only.
|
|
22
|
-
#
|
|
23
|
-
# Atlas refuses with a `422` (`has_children`) while the Community
|
|
24
|
-
# still holds a Collection or a sub-community, **including
|
|
25
|
-
# tombstoned ones**. That is stricter than tombstone, which counts
|
|
26
|
-
# only live members: a member left behind by a purge is orphaned
|
|
27
|
-
# for good. Empty the tree leaf-first.
|
|
28
|
-
#
|
|
29
|
-
# @param id [String] the Community ID.
|
|
30
|
-
# @param confirm [Symbol] must be `:i_understand`. Any other value
|
|
31
|
-
# raises `ArgumentError`.
|
|
32
|
-
# @param nuid [String, nil] optional acting user's NUID.
|
|
33
|
-
# @param on_behalf_of [String, nil] optional `On-Behalf-Of` NUID.
|
|
34
|
-
# @return [Faraday::Response] the raw delete response.
|
|
35
|
-
# @raise [ArgumentError] if `confirm:` is missing or not the
|
|
36
|
-
# sentinel value.
|
|
37
|
-
#
|
|
38
|
-
# @example
|
|
39
|
-
# AtlasRb::Admin::Community.destroy("c-123", confirm: :i_understand)
|
|
40
|
-
def self.destroy(id, confirm:, nuid: nil, on_behalf_of: nil)
|
|
41
|
-
unless confirm == :i_understand
|
|
42
|
-
raise ArgumentError,
|
|
43
|
-
"AtlasRb::Admin::Community.destroy requires confirm: :i_understand"
|
|
44
|
-
end
|
|
45
|
-
connection({}, nuid, on_behalf_of: on_behalf_of).delete(ROUTE + id)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Restore a previously-tombstoned Community.
|
|
49
|
-
#
|
|
50
|
-
# @param id [String] the Community ID.
|
|
51
|
-
# @param nuid [String, nil] optional acting user's NUID.
|
|
52
|
-
# @param on_behalf_of [String, nil] optional `On-Behalf-Of` NUID.
|
|
53
|
-
# @return [Faraday::Response] the raw response.
|
|
54
|
-
#
|
|
55
|
-
# @example
|
|
56
|
-
# AtlasRb::Admin::Community.restore("c-123")
|
|
57
|
-
def self.restore(id, nuid: nil, on_behalf_of: nil)
|
|
58
|
-
connection({}, nuid, on_behalf_of: on_behalf_of).post(ROUTE + id + '/restore')
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
data/lib/atlas_rb/admin/work.rb
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module AtlasRb
|
|
4
|
-
module Admin
|
|
5
|
-
# Destructive lifecycle operations on a {AtlasRb::Work}.
|
|
6
|
-
#
|
|
7
|
-
# See {AtlasRb::Admin} for the rationale behind the namespace and the
|
|
8
|
-
# `confirm: :i_understand` friction marker.
|
|
9
|
-
class Work
|
|
10
|
-
extend AtlasRb::FaradayHelper
|
|
11
|
-
|
|
12
|
-
# Atlas REST endpoint prefix.
|
|
13
|
-
# @api private
|
|
14
|
-
ROUTE = "/works/"
|
|
15
|
-
|
|
16
|
-
# Hard-delete a Work — a purge, not a withdrawal.
|
|
17
|
-
#
|
|
18
|
-
# Removes the Work's metadata, cascades into its FileSets and their
|
|
19
|
-
# Blobs, and removes the OCFL objects holding the preserved bytes:
|
|
20
|
-
# every retained revision, not only the current one. Nothing
|
|
21
|
-
# survives but the audit row, which records the NOIDs it removed.
|
|
22
|
-
#
|
|
23
|
-
# Unrecoverable — prefer {AtlasRb::Work.tombstone} for the
|
|
24
|
-
# user-visible withdrawal path, which keeps everything and can be
|
|
25
|
-
# reversed. Admin-only.
|
|
26
|
-
#
|
|
27
|
-
# @param id [String] the Work ID.
|
|
28
|
-
# @param confirm [Symbol] must be `:i_understand`. Any other value
|
|
29
|
-
# (including the kwarg being omitted) raises `ArgumentError`.
|
|
30
|
-
# @param nuid [String, nil] optional acting user's NUID. Falls
|
|
31
|
-
# through to {AtlasRb.config}.default_nuid when omitted.
|
|
32
|
-
# @param on_behalf_of [String, nil] optional NUID for the
|
|
33
|
-
# `On-Behalf-Of` header. Falls through to
|
|
34
|
-
# {AtlasRb.config}.default_on_behalf_of when omitted.
|
|
35
|
-
# @return [Faraday::Response] the raw delete response.
|
|
36
|
-
# @raise [ArgumentError] if `confirm:` is missing or not the
|
|
37
|
-
# sentinel value.
|
|
38
|
-
#
|
|
39
|
-
# @example
|
|
40
|
-
# AtlasRb::Admin::Work.destroy("w-789", confirm: :i_understand)
|
|
41
|
-
def self.destroy(id, confirm:, nuid: nil, on_behalf_of: nil)
|
|
42
|
-
unless confirm == :i_understand
|
|
43
|
-
raise ArgumentError,
|
|
44
|
-
"AtlasRb::Admin::Work.destroy requires confirm: :i_understand"
|
|
45
|
-
end
|
|
46
|
-
connection({}, nuid, on_behalf_of: on_behalf_of).delete(ROUTE + id)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Restore a previously-tombstoned Work.
|
|
50
|
-
#
|
|
51
|
-
# Reverses a withdrawal: search and show pages stop returning a
|
|
52
|
-
# withdrawn stub. Operator-only; typically driven from a Rails
|
|
53
|
-
# console or a future admin panel after the library has decided a
|
|
54
|
-
# withdrawn Work should come back.
|
|
55
|
-
#
|
|
56
|
-
# @param id [String] the Work ID.
|
|
57
|
-
# @param nuid [String, nil] optional acting user's NUID. Falls
|
|
58
|
-
# through to {AtlasRb.config}.default_nuid when omitted.
|
|
59
|
-
# @param on_behalf_of [String, nil] optional NUID for the
|
|
60
|
-
# `On-Behalf-Of` header. Falls through to
|
|
61
|
-
# {AtlasRb.config}.default_on_behalf_of when omitted.
|
|
62
|
-
# @return [Faraday::Response] the raw response.
|
|
63
|
-
#
|
|
64
|
-
# @example
|
|
65
|
-
# AtlasRb::Admin::Work.restore("w-789")
|
|
66
|
-
def self.restore(id, nuid: nil, on_behalf_of: nil)
|
|
67
|
-
connection({}, nuid, on_behalf_of: on_behalf_of).post(ROUTE + id + '/restore')
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|