atlas_rb 1.10.1 → 1.11.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 +58 -0
- data/Gemfile.lock +1 -1
- data/lib/atlas_rb/admin/collection.rb +10 -2
- data/lib/atlas_rb/admin/community.rb +10 -2
- data/lib/atlas_rb/admin/work.rb +9 -4
- data/lib/atlas_rb/blob.rb +6 -1
- data/lib/atlas_rb/errors.rb +32 -0
- data/lib/atlas_rb/file_set.rb +6 -1
- data/lib/atlas_rb/middleware/raise_on_resource_error.rb +13 -3
- data/lib/atlas_rb/work.rb +123 -0
- 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: 58d8d5524e43dc1f14f172ea65bc1e78e55826a63c814f2ef517675d07732d08
|
|
4
|
+
data.tar.gz: 548d4cba38f7a2ef81f210612157ee36a56de6e15b5fcff367b4df1b73fda847
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 745c494de1bd2da2a0048e2d3d562961bed6e48058b30da25dcbcdc0f9b2b7f3159c2b831508ac972a968dc6f02ee2a7f0a5cf11b85e2319277d1f200adf51f4
|
|
7
|
+
data.tar.gz: fe90db7467cdae4726357c2ba6d0d939d50c4174f1a3aa43ee8fd91adea19923fa321f53804fb52ed8db39eab22b578dadc6e542f77d54d1e1177e1effab0c53
|
data/.version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.11.0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,63 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.11.0
|
|
4
|
+
|
|
5
|
+
### Added — `Work.associations` / `.associate` / `.disassociate`
|
|
6
|
+
|
|
7
|
+
DRS v1 let a depositor declare that one object is the codebook, figure,
|
|
8
|
+
transcription, instructional material or supplemental material **for** another.
|
|
9
|
+
The two objects stayed separate records, and each one's page showed the link
|
|
10
|
+
from its own end. Atlas now records this again, and these three bindings reach
|
|
11
|
+
it.
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
AtlasRb::Work.associate(codebook_id, dataset_id, type: "is_codebook_for")
|
|
15
|
+
AtlasRb::Work.associations(dataset_id)
|
|
16
|
+
# => {"outbound" => {}, "inbound" => {"is_codebook_for" => ["w-codebook"]}}
|
|
17
|
+
AtlasRb::Work.disassociate(codebook_id, dataset_id, type: "is_codebook_for")
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
All three return the same shape, so a mutation needs no follow-up read.
|
|
21
|
+
`outbound` is what the Work asserts; `inbound` is what other Works assert about
|
|
22
|
+
it. The edge is stored once, on the asserting Work, and Atlas derives the other
|
|
23
|
+
direction — so the two can never drift apart.
|
|
24
|
+
|
|
25
|
+
The five predicates are in `AtlasRb::Work::ASSOCIATION_TYPES`, for building a
|
|
26
|
+
select box without hard-coding the vocabulary. A sixth needs an Atlas release,
|
|
27
|
+
so the list cannot get ahead of the server.
|
|
28
|
+
|
|
29
|
+
Writes are admin / devolved-admin only: the claim renders on the target's page
|
|
30
|
+
too, and the asserter often holds no rights over it.
|
|
31
|
+
|
|
32
|
+
### Added — `WorkAssociationError`
|
|
33
|
+
|
|
34
|
+
A `422` on an association path now raises a typed error carrying `#code`
|
|
35
|
+
(`invalid_type`, `target_not_found`, `invalid_target_type`,
|
|
36
|
+
`self_association`, `tombstoned_work`, `tombstoned_target`). A `403` raises the
|
|
37
|
+
existing `ForbiddenError`. Without these the binding would return the envelope
|
|
38
|
+
as if it were a success and the rejection would be silently discarded.
|
|
39
|
+
|
|
40
|
+
### Changed — the `destroy` docstrings now describe a purge
|
|
41
|
+
|
|
42
|
+
`Admin::{Work,Collection,Community}.destroy`, `FileSet.destroy` and
|
|
43
|
+
`Blob.destroy` previously promised more than the server did: the docstrings
|
|
44
|
+
claimed a cascade and removal "from Atlas storage" while the server deleted one
|
|
45
|
+
Postgres row and one Solr document, leaving every byte on disk.
|
|
46
|
+
|
|
47
|
+
Atlas has since made `destroy` mean what the docstrings said, and they are
|
|
48
|
+
updated to match what it now does — including the parts they never mentioned:
|
|
49
|
+
|
|
50
|
+
- Deleting a Blob or a FileSet removes the **whole OCFL object**, so every
|
|
51
|
+
retained revision goes, not only the current one. `versions` and `rollback`
|
|
52
|
+
have nothing left to work with afterwards.
|
|
53
|
+
- `Admin::Collection.destroy` and `Admin::Community.destroy` refuse with a
|
|
54
|
+
`422` (`has_children`) while the container still holds a member, **including
|
|
55
|
+
a tombstoned one**. That is stricter than `tombstone`, which counts only live
|
|
56
|
+
members: a purge cannot be undone, so a member left behind is orphaned for
|
|
57
|
+
good.
|
|
58
|
+
|
|
59
|
+
No signature changed; this release is additive.
|
|
60
|
+
|
|
3
61
|
## 1.10.1
|
|
4
62
|
|
|
5
63
|
### Added — `Work.mark_incomplete` / `Work.clear_incomplete`
|
data/Gemfile.lock
CHANGED
|
@@ -13,10 +13,18 @@ module AtlasRb
|
|
|
13
13
|
# @api private
|
|
14
14
|
ROUTE = "/collections/"
|
|
15
15
|
|
|
16
|
-
# Hard-delete a Collection.
|
|
16
|
+
# Hard-delete a Collection — a purge, not a withdrawal.
|
|
17
17
|
#
|
|
18
|
+
# Removes the Collection's metadata, its descriptive-metadata
|
|
19
|
+
# FileSet, and the OCFL objects holding the preserved bytes.
|
|
18
20
|
# Unrecoverable — prefer {AtlasRb::Collection.tombstone} for
|
|
19
|
-
# user-visible withdrawal.
|
|
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.
|
|
20
28
|
#
|
|
21
29
|
# @param id [String] the Collection ID.
|
|
22
30
|
# @param confirm [Symbol] must be `:i_understand`. Any other value
|
|
@@ -13,10 +13,18 @@ module AtlasRb
|
|
|
13
13
|
# @api private
|
|
14
14
|
ROUTE = "/communities/"
|
|
15
15
|
|
|
16
|
-
# Hard-delete a Community.
|
|
16
|
+
# Hard-delete a Community — a purge, not a withdrawal.
|
|
17
17
|
#
|
|
18
|
+
# Removes the Community's metadata, its descriptive-metadata
|
|
19
|
+
# FileSet, and the OCFL objects holding the preserved bytes.
|
|
18
20
|
# Unrecoverable — prefer {AtlasRb::Community.tombstone} for
|
|
19
|
-
# user-visible withdrawal.
|
|
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.
|
|
20
28
|
#
|
|
21
29
|
# @param id [String] the Community ID.
|
|
22
30
|
# @param confirm [Symbol] must be `:i_understand`. Any other value
|
data/lib/atlas_rb/admin/work.rb
CHANGED
|
@@ -13,11 +13,16 @@ module AtlasRb
|
|
|
13
13
|
# @api private
|
|
14
14
|
ROUTE = "/works/"
|
|
15
15
|
|
|
16
|
-
# Hard-delete a Work.
|
|
16
|
+
# Hard-delete a Work — a purge, not a withdrawal.
|
|
17
17
|
#
|
|
18
|
-
# Removes the Work, its FileSets
|
|
19
|
-
#
|
|
20
|
-
#
|
|
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.
|
|
21
26
|
#
|
|
22
27
|
# @param id [String] the Work ID.
|
|
23
28
|
# @param confirm [Symbol] must be `:i_understand`. Any other value
|
data/lib/atlas_rb/blob.rb
CHANGED
|
@@ -186,7 +186,12 @@ module AtlasRb
|
|
|
186
186
|
end
|
|
187
187
|
end
|
|
188
188
|
|
|
189
|
-
# Delete a Blob
|
|
189
|
+
# Delete a Blob: the metadata record **and** the bytes.
|
|
190
|
+
#
|
|
191
|
+
# Atlas removes the whole OCFL object, so **every retained revision** goes,
|
|
192
|
+
# not only the current one — {.versions} and {.rollback} have nothing left
|
|
193
|
+
# to work with afterwards. Unrecoverable, and admin-only. The Blob is also
|
|
194
|
+
# unlinked from its FileSet, whose METS is rebuilt.
|
|
190
195
|
#
|
|
191
196
|
# @param id [String] the Blob ID.
|
|
192
197
|
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
data/lib/atlas_rb/errors.rb
CHANGED
|
@@ -101,6 +101,38 @@ module AtlasRb
|
|
|
101
101
|
end
|
|
102
102
|
end
|
|
103
103
|
|
|
104
|
+
# Raised when Atlas rejects a Work-association write
|
|
105
|
+
# (`POST` / `DELETE /works/:id/associations...`) with a `422` carrying a
|
|
106
|
+
# machine-readable `error` discriminator — `invalid_type` (not one of the
|
|
107
|
+
# five predicates), `target_not_found`, `invalid_target_type` (the target is
|
|
108
|
+
# not a Work), `self_association`, `tombstoned_work`, or `tombstoned_target`.
|
|
109
|
+
#
|
|
110
|
+
# The association sibling of {LinkedMemberError}; same shape, same rationale
|
|
111
|
+
# (the binding would otherwise discard the envelope on a non-2xx).
|
|
112
|
+
#
|
|
113
|
+
# rescue AtlasRb::WorkAssociationError => e
|
|
114
|
+
# flash.now[:alert] = t("associations.errors.#{e.code}", default: e.message)
|
|
115
|
+
#
|
|
116
|
+
# @note Authorization failures surface as {ForbiddenError} (HTTP 403) —
|
|
117
|
+
# asserting an association is admin / devolved-admin only.
|
|
118
|
+
class WorkAssociationError < Error
|
|
119
|
+
# @return [String, nil] the machine-readable error code from the envelope,
|
|
120
|
+
# suitable for keying an i18n map.
|
|
121
|
+
attr_reader :code
|
|
122
|
+
|
|
123
|
+
# @return [String, nil] the rejected Work's ID, from the envelope.
|
|
124
|
+
attr_reader :resource_id
|
|
125
|
+
|
|
126
|
+
# @param message [String] human-readable rejection description.
|
|
127
|
+
# @param code [String, nil] the envelope's `error` discriminator.
|
|
128
|
+
# @param resource_id [String, nil] the rejected Work's ID.
|
|
129
|
+
def initialize(message, code: nil, resource_id: nil)
|
|
130
|
+
super(message)
|
|
131
|
+
@code = code
|
|
132
|
+
@resource_id = resource_id
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
104
136
|
# Raised when Atlas rejects a Compilation (Set) write with a `422`
|
|
105
137
|
# carrying a machine-readable `error` discriminator — a blank title on
|
|
106
138
|
# create/update (`invalid_record`), or a membership add whose noid does
|
data/lib/atlas_rb/file_set.rb
CHANGED
|
@@ -84,7 +84,12 @@ module AtlasRb
|
|
|
84
84
|
))["file_set"]
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
-
# Delete a FileSet.
|
|
87
|
+
# Delete a FileSet, its Blobs, and their bytes.
|
|
88
|
+
#
|
|
89
|
+
# Atlas cascades into the FileSet's Blobs and removes each one's OCFL
|
|
90
|
+
# object, so every retained revision of those binaries goes too.
|
|
91
|
+
# Unrecoverable, and admin-only. The parent Work's METS structMap is
|
|
92
|
+
# rebuilt without the removed pages.
|
|
88
93
|
#
|
|
89
94
|
# @param id [String] the FileSet ID.
|
|
90
95
|
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
|
@@ -15,7 +15,8 @@ module AtlasRb
|
|
|
15
15
|
# {RaiseOnStaleResource}.
|
|
16
16
|
#
|
|
17
17
|
# It is intentionally narrow — it only fires on the re-parent
|
|
18
|
-
# (`.../parent`)
|
|
18
|
+
# (`.../parent`), linked-member (`.../linked_members...`) and association
|
|
19
|
+
# (`.../associations...`) write paths,
|
|
19
20
|
# the Compilation surface (`/compilations...`), the derivative-permissions
|
|
20
21
|
# write (`.../derivative_permissions`), the container-create endpoints
|
|
21
22
|
# ({CREATE_PATHS}), and binary uploads (`/files...`, `/file_sets...`), and
|
|
@@ -37,6 +38,7 @@ module AtlasRb
|
|
|
37
38
|
# - `403` on a re-parent/linked/Compilation/derivative-permissions/create path → {AtlasRb::ForbiddenError}
|
|
38
39
|
# - `422` on `.../parent` → {AtlasRb::ReparentError} (`error`/`resource_id`)
|
|
39
40
|
# - `422` on `.../linked_members...` → {AtlasRb::LinkedMemberError}
|
|
41
|
+
# - `422` on `.../associations...` → {AtlasRb::WorkAssociationError}
|
|
40
42
|
# - `422` on `/compilations...` → {AtlasRb::CompilationError}
|
|
41
43
|
# - `422` on `.../derivative_permissions` → {AtlasRb::DerivativePermissionsError}
|
|
42
44
|
# - `422` + an ACL discriminator anywhere → {AtlasRb::PermissionsError}
|
|
@@ -63,6 +65,7 @@ module AtlasRb
|
|
|
63
65
|
# @raise [AtlasRb::ForbiddenError] on a 403 to a re-parent/linked/Compilation/create path.
|
|
64
66
|
# @raise [AtlasRb::ReparentError] on a 422 to a re-parent path.
|
|
65
67
|
# @raise [AtlasRb::LinkedMemberError] on a 422 to a linked-member path.
|
|
68
|
+
# @raise [AtlasRb::WorkAssociationError] on a 422 to an association path.
|
|
66
69
|
# @raise [AtlasRb::CompilationError] on a 422 to a Compilation path.
|
|
67
70
|
# @raise [AtlasRb::DerivativePermissionsError] on a 422 to a derivative-permissions path.
|
|
68
71
|
# @raise [AtlasRb::PermissionsError] on a 422 carrying an ACL-invariant discriminator.
|
|
@@ -74,6 +77,7 @@ module AtlasRb
|
|
|
74
77
|
path = env.url&.path.to_s
|
|
75
78
|
reparent = path.end_with?("/parent")
|
|
76
79
|
linked = path.include?("/linked_members")
|
|
80
|
+
association = path.include?("/associations")
|
|
77
81
|
compilation = path.start_with?("/compilations")
|
|
78
82
|
deriv_perms = path.end_with?("/derivative_permissions")
|
|
79
83
|
create = env.method.to_s == "post" && CREATE_PATHS.include?(path.chomp("/"))
|
|
@@ -92,11 +96,11 @@ module AtlasRb
|
|
|
92
96
|
)
|
|
93
97
|
end
|
|
94
98
|
|
|
95
|
-
return unless reparent || linked || compilation || deriv_perms || create || upload
|
|
99
|
+
return unless reparent || linked || association || compilation || deriv_perms || create || upload
|
|
96
100
|
|
|
97
101
|
if env.status == 403
|
|
98
102
|
# 403s on upload paths stay raw — acting-as/authz isn't an upload concern here.
|
|
99
|
-
return unless reparent || linked || compilation || deriv_perms || create
|
|
103
|
+
return unless reparent || linked || association || compilation || deriv_perms || create
|
|
100
104
|
|
|
101
105
|
raise AtlasRb::ForbiddenError.new(
|
|
102
106
|
body["message"] || "Atlas refused the request",
|
|
@@ -116,6 +120,12 @@ module AtlasRb
|
|
|
116
120
|
code: body["error"],
|
|
117
121
|
resource_id: body["resource_id"]
|
|
118
122
|
)
|
|
123
|
+
elsif association
|
|
124
|
+
raise AtlasRb::WorkAssociationError.new(
|
|
125
|
+
body["message"] || "Atlas rejected the association write",
|
|
126
|
+
code: body["error"],
|
|
127
|
+
resource_id: body["resource_id"]
|
|
128
|
+
)
|
|
119
129
|
elsif compilation
|
|
120
130
|
raise AtlasRb::CompilationError.new(
|
|
121
131
|
body["message"] || "Atlas rejected the compilation write",
|
data/lib/atlas_rb/work.rb
CHANGED
|
@@ -799,5 +799,128 @@ module AtlasRb
|
|
|
799
799
|
.delete(ROUTE + work_id + '/linked_members/' + collection_id)
|
|
800
800
|
)
|
|
801
801
|
end
|
|
802
|
+
|
|
803
|
+
# The five relationship predicates Atlas accepts, carried here so a caller
|
|
804
|
+
# can build a select box without hard-coding the vocabulary. Adding a sixth
|
|
805
|
+
# needs an Atlas release, so this list cannot drift ahead of the server.
|
|
806
|
+
ASSOCIATION_TYPES = %w[
|
|
807
|
+
is_codebook_for
|
|
808
|
+
is_figure_for
|
|
809
|
+
is_instructional_material_for
|
|
810
|
+
is_supplemental_material_for
|
|
811
|
+
is_transcription_of
|
|
812
|
+
].freeze
|
|
813
|
+
|
|
814
|
+
# List a Work's typed associations with other Works.
|
|
815
|
+
#
|
|
816
|
+
# Wraps `GET /works/<id>/associations`. An association is DRS v1's
|
|
817
|
+
# "associated works": a directed claim that one object is the codebook,
|
|
818
|
+
# figure, transcription, instructional material or supplemental material
|
|
819
|
+
# **for** another. Both objects stay separate records — this is not
|
|
820
|
+
# membership, and nothing moves in the containment tree.
|
|
821
|
+
#
|
|
822
|
+
# The edge is stored once, on the Work that asserts it. Atlas derives the
|
|
823
|
+
# other direction, so `outbound` and `inbound` can never disagree.
|
|
824
|
+
#
|
|
825
|
+
# @param id [String] the Work ID.
|
|
826
|
+
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
|
827
|
+
# path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
|
|
828
|
+
# path it is ignored (identity lives in the token).
|
|
829
|
+
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
830
|
+
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
831
|
+
# omitted.
|
|
832
|
+
# @return [Hash] `{"outbound" => {predicate => [noid, …]}, "inbound" => {…}}`.
|
|
833
|
+
# `outbound` is what this Work asserts, `inbound` what other Works assert
|
|
834
|
+
# about it. Predicates holding no edges are omitted, so both maps are
|
|
835
|
+
# `{}` for an unassociated Work.
|
|
836
|
+
#
|
|
837
|
+
# @example
|
|
838
|
+
# AtlasRb::Work.associations("w-789")
|
|
839
|
+
# # => {"outbound" => {"is_codebook_for" => ["w-123"]}, "inbound" => {}}
|
|
840
|
+
def self.associations(id, nuid: nil, on_behalf_of: nil)
|
|
841
|
+
JSON.parse(
|
|
842
|
+
connection({}, nuid, on_behalf_of: on_behalf_of).get(ROUTE + id + '/associations')&.body
|
|
843
|
+
)
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
# Assert that this Work stands in a typed relationship to another Work.
|
|
847
|
+
#
|
|
848
|
+
# Wraps `POST /works/<id>/associations` with a `work_id` + `type` body.
|
|
849
|
+
# The edge is stored on **this** Work only; `target` reports the same edge
|
|
850
|
+
# under `inbound`. Asserting an edge that already exists is a no-op, and
|
|
851
|
+
# two Works can hold several different edges at once.
|
|
852
|
+
#
|
|
853
|
+
# A cycle is permitted and meaningful — "A is a transcription of B" and
|
|
854
|
+
# "B is a figure for A" can both be true.
|
|
855
|
+
#
|
|
856
|
+
# @param work_id [String] the asserting Work's ID (the codebook, figure, …).
|
|
857
|
+
# @param target_id [String] the Work being pointed at (the dataset, article, …).
|
|
858
|
+
# @param type [String] one of {ASSOCIATION_TYPES}.
|
|
859
|
+
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
|
860
|
+
# path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
|
|
861
|
+
# path it is ignored (identity lives in the token).
|
|
862
|
+
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
863
|
+
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
864
|
+
# omitted.
|
|
865
|
+
# @return [Hash] the Work's associations *after* the add, in the
|
|
866
|
+
# {.associations} shape — so no follow-up GET is needed.
|
|
867
|
+
# @raise [AtlasRb::WorkAssociationError] if Atlas rejects the claim (HTTP
|
|
868
|
+
# 422): unknown type, unresolvable target, a non-Work target, the Work
|
|
869
|
+
# itself, or either end tombstoned. The envelope's `error` code is
|
|
870
|
+
# exposed as `#code`.
|
|
871
|
+
# @raise [AtlasRb::ForbiddenError] if Atlas refuses the write (HTTP 403).
|
|
872
|
+
# Associating is admin / devolved-admin only, because the claim renders
|
|
873
|
+
# on the target's page too.
|
|
874
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no
|
|
875
|
+
# such Work, so the write did not happen.
|
|
876
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's
|
|
877
|
+
# status and body.
|
|
878
|
+
#
|
|
879
|
+
# @example
|
|
880
|
+
# AtlasRb::Work.associate("w-789", "w-123", type: "is_codebook_for")
|
|
881
|
+
# # => {"outbound" => {"is_codebook_for" => ["w-123"]}, "inbound" => {}}
|
|
882
|
+
def self.associate(work_id, target_id, type:, nuid: nil, on_behalf_of: nil)
|
|
883
|
+
write_resource(
|
|
884
|
+
connection({ work_id: target_id, type: type }, nuid, on_behalf_of: on_behalf_of)
|
|
885
|
+
.post(ROUTE + work_id + '/associations')
|
|
886
|
+
)
|
|
887
|
+
end
|
|
888
|
+
|
|
889
|
+
# Retract one typed relationship between two Works.
|
|
890
|
+
#
|
|
891
|
+
# Wraps `DELETE /works/<id>/associations/<type>/<target_id>` — the type is
|
|
892
|
+
# a path segment because it is part of the edge's identity, so retracting
|
|
893
|
+
# the figure claim leaves a transcription claim between the same two Works
|
|
894
|
+
# standing. Idempotent: retracting an edge that was never asserted is a
|
|
895
|
+
# no-op.
|
|
896
|
+
#
|
|
897
|
+
# @param work_id [String] the asserting Work's ID.
|
|
898
|
+
# @param target_id [String] the associated Work to drop.
|
|
899
|
+
# @param type [String] the predicate to retract, one of {ASSOCIATION_TYPES}.
|
|
900
|
+
# @param nuid [String, nil] optional acting user's NUID. On the relay-signing
|
|
901
|
+
# path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
|
|
902
|
+
# path it is ignored (identity lives in the token).
|
|
903
|
+
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
904
|
+
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
905
|
+
# omitted.
|
|
906
|
+
# @return [Hash] the Work's remaining associations, in the {.associations}
|
|
907
|
+
# shape.
|
|
908
|
+
# @raise [AtlasRb::WorkAssociationError] if Atlas rejects the write (HTTP
|
|
909
|
+
# 422) — an unresolvable target is the usual cause here.
|
|
910
|
+
# @raise [AtlasRb::ForbiddenError] if Atlas refuses the write (HTTP 403).
|
|
911
|
+
# @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no
|
|
912
|
+
# such Work, so the write did not happen.
|
|
913
|
+
# @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's
|
|
914
|
+
# status and body.
|
|
915
|
+
#
|
|
916
|
+
# @example
|
|
917
|
+
# AtlasRb::Work.disassociate("w-789", "w-123", type: "is_codebook_for")
|
|
918
|
+
# # => {"outbound" => {}, "inbound" => {}}
|
|
919
|
+
def self.disassociate(work_id, target_id, type:, nuid: nil, on_behalf_of: nil)
|
|
920
|
+
write_resource(
|
|
921
|
+
connection({}, nuid, on_behalf_of: on_behalf_of)
|
|
922
|
+
.delete(ROUTE + work_id + '/associations/' + type.to_s + '/' + target_id)
|
|
923
|
+
)
|
|
924
|
+
end
|
|
802
925
|
end
|
|
803
926
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: atlas_rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Cliff
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|