atlas_rb 1.2.2 → 1.3.1
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 +62 -1
- data/Gemfile.lock +1 -1
- data/README.md +59 -0
- data/lib/atlas_rb/resource.rb +114 -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: '0947add262eb2b9b4ab993f16c2c379877455e9ece3bca04f41a61bfddf79ee3'
|
|
4
|
+
data.tar.gz: d239887dfaac21e78a7118503d4ed8ab79b12b3aa174ee18f5a8f31f253ca866
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b4d59334c7730d11f9f7779b1cbef5c7cef49ce4b1726257a9c8590f258ab4c8aac80ffffa10c1111a38b4d98b95c7b36e824eab925aff4aae1ff87336f7e37e
|
|
7
|
+
data.tar.gz: a5c0484e6d4204758dc1eee8e2ca6086a289e19e250a404fbdffc4af65eaa7864f2da7f93a9586d4d84776c76b33dbdebb5147ae4d728baed51110864c347e30
|
data/.version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.3.1
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,67 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 1.3.1
|
|
4
|
+
|
|
5
|
+
### Added — `AtlasRb::Resource.mods_versions` / `mods_version` (MODS version history)
|
|
6
|
+
|
|
7
|
+
Two bindings for Atlas's MODS version-history endpoints. `mods_versions`
|
|
8
|
+
lists the retained versions of a resource's descriptive metadata;
|
|
9
|
+
`mods_version` fetches the raw MODS XML as of a specific version — together
|
|
10
|
+
enough to drive a line-diff between any two MODS states.
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
history = AtlasRb::Resource.mods_versions("w-789")
|
|
14
|
+
history["versions"].first["version_id"] # => "v5" (newest)
|
|
15
|
+
history["versions"].first["actor_nuid"] # => "000000002"
|
|
16
|
+
|
|
17
|
+
old_xml = AtlasRb::Resource.mods_version("w-789", "v3")
|
|
18
|
+
new_xml = AtlasRb::Resource.mods_version("w-789", "v5")
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- `mods_versions` returns the full envelope (`resource_id` + a
|
|
22
|
+
reverse-chronological `versions` array) as an `AtlasRb::Mash`. Each
|
|
23
|
+
descriptor mirrors the audit-event shape (`version_id`, `created`,
|
|
24
|
+
`actor_nuid`, `on_behalf_of_nuid`, `source`, `note`); actor fields are
|
|
25
|
+
correlated from the audit log and may be `null`. Admin-gated server-side.
|
|
26
|
+
- `mods_version` returns the **raw XML body** (mirroring `Work.mods`). Only
|
|
27
|
+
XML is version-recoverable — the JSON access copy is overwritten in place
|
|
28
|
+
— so `kind:` is accepted for parity but XML is the only retained format.
|
|
29
|
+
- Version labels are opaque, sortable OCFL `vN` strings (a Blob's
|
|
30
|
+
preservation envelope occupies earlier versions, so the first MODS
|
|
31
|
+
version is typically `v3`). Treat them as identifiers to feed back into
|
|
32
|
+
`mods_version`, not as ordinals.
|
|
33
|
+
- Both are type-agnostic (Community / Collection / Work) and live on
|
|
34
|
+
`Resource` beside `history` / `permissions`. A resource with no MODS
|
|
35
|
+
returns `{ "versions" => [] }`.
|
|
36
|
+
|
|
37
|
+
## 1.3.0
|
|
38
|
+
|
|
39
|
+
### Added — `AtlasRb::Resource.find_many` (batch resolve by NOID)
|
|
40
|
+
|
|
41
|
+
A binding for Atlas's `POST /resources/find_many`. Resolves a set of NOIDs
|
|
42
|
+
to lightweight digests in **one** round-trip, replacing the `find`-per-id
|
|
43
|
+
fan-out that several Cerberus surfaces (breadcrumbs, linked-member lists,
|
|
44
|
+
load-destination pickers) paid on every render.
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
nodes = AtlasRb::Resource.find_many(["col-456", "col-457", "missing"])
|
|
48
|
+
by_noid = nodes.index_by { |n| n["noid"] }
|
|
49
|
+
by_noid["col-456"].title # => "Some Collection"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- Each digest is `{ "id", "noid", "klass", "title", "thumbnail",
|
|
53
|
+
"tombstoned" }` — not the full typed payload. `title` / `thumbnail` are
|
|
54
|
+
`null` for resources off the Modsable backbone (FileSet/Blob).
|
|
55
|
+
- The ids ride in the request **body**, so the list isn't bounded by URL
|
|
56
|
+
length. Returns one `AtlasRb::Mash` per resolved resource.
|
|
57
|
+
- The result is **unordered** and **may be shorter than the input**:
|
|
58
|
+
unresolvable ids are dropped, tombstoned ones come back flagged
|
|
59
|
+
(`"tombstoned" => true`). Index by `"noid"` — don't assume positional
|
|
60
|
+
correspondence with the input.
|
|
61
|
+
- Resolves NOIDs (alternate ids) only; raw Valkyrie ids are not a supported
|
|
62
|
+
input.
|
|
63
|
+
|
|
64
|
+
## 1.2.2
|
|
4
65
|
|
|
5
66
|
### Added — `AtlasRb::AuditEvent.emit` (session-scoped audit events)
|
|
6
67
|
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -245,6 +245,44 @@ mode-less session events. Atlas stamps `occurred_at` server-side.
|
|
|
245
245
|
Authorization errors (`401` / `403`) surface as raw Faraday responses,
|
|
246
246
|
matching `Resource.history`.
|
|
247
247
|
|
|
248
|
+
### MODS version history
|
|
249
|
+
|
|
250
|
+
Every descriptive-metadata edit retains the prior MODS XML on the server.
|
|
251
|
+
`Resource.mods_versions` lists the retained versions; `Resource.mods_version`
|
|
252
|
+
fetches the raw MODS XML as of one of them — together enough to render a
|
|
253
|
+
line-diff between any two MODS states. Both are type-agnostic (pass any
|
|
254
|
+
Community, Collection, or Work ID).
|
|
255
|
+
|
|
256
|
+
```ruby
|
|
257
|
+
history = AtlasRb::Resource.mods_versions("w-789")
|
|
258
|
+
history["resource_id"] # => "w-789"
|
|
259
|
+
history["versions"].first["version_id"] # => "v5" (newest first)
|
|
260
|
+
history["versions"].first["actor_nuid"] # => "000000002" (or nil)
|
|
261
|
+
|
|
262
|
+
# Fetch two versions and diff them:
|
|
263
|
+
old_xml = AtlasRb::Resource.mods_version("w-789", "v3")
|
|
264
|
+
new_xml = AtlasRb::Resource.mods_version("w-789", "v5")
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
`mods_versions` returns the full envelope (`resource_id` + a
|
|
268
|
+
reverse-chronological `versions` array) as an `AtlasRb::Mash`; each
|
|
269
|
+
descriptor mirrors the audit-event shape (`version_id`, `created`,
|
|
270
|
+
`actor_nuid`, `on_behalf_of_nuid`, `source`, `note`), so the two streams
|
|
271
|
+
render with the same helpers. Actor attribution is correlated from the
|
|
272
|
+
audit log and may be `null`. The endpoint is admin-gated server-side
|
|
273
|
+
(it exposes edit attribution).
|
|
274
|
+
|
|
275
|
+
`mods_version` returns the **raw XML body** — like `Work.mods`, not a Mash.
|
|
276
|
+
Only XML is version-recoverable (the JSON access copy is overwritten in
|
|
277
|
+
place), so the server serves historical XML; `kind:` is accepted for parity
|
|
278
|
+
with `Work.mods` but XML is the only retained format.
|
|
279
|
+
|
|
280
|
+
Version labels are **opaque, sortable OCFL `vN` strings**, not a 1-based
|
|
281
|
+
counter — a Blob's preservation envelope occupies earlier versions, so the
|
|
282
|
+
first MODS version is typically `v3`. Treat them as identifiers to feed back
|
|
283
|
+
into `mods_version`. A resource with no MODS returns `{ "versions" => [] }`;
|
|
284
|
+
`401` / `403` surface as raw Faraday responses, matching `Resource.history`.
|
|
285
|
+
|
|
248
286
|
### Re-parenting
|
|
249
287
|
|
|
250
288
|
`reparent` moves a resource to a new structural parent, binding Atlas's
|
|
@@ -312,6 +350,27 @@ The two mutations raise the same way `reparent` does — `LinkedMemberError`
|
|
|
312
350
|
on a structural `422` (carrying the envelope's `error` code as `#code`) and
|
|
313
351
|
`ForbiddenError` on a `403` — instead of swallowing the envelope.
|
|
314
352
|
|
|
353
|
+
### Batch resolve (`Resource.find_many`)
|
|
354
|
+
|
|
355
|
+
When you have a *set* of NOIDs and only need each one's title / klass /
|
|
356
|
+
thumbnail — breadcrumb chains, linked-member lists, load-destination
|
|
357
|
+
pickers — resolve them in one round-trip instead of a `find`-per-id
|
|
358
|
+
fan-out:
|
|
359
|
+
|
|
360
|
+
```ruby
|
|
361
|
+
nodes = AtlasRb::Resource.find_many(["col-456", "col-457", "missing"])
|
|
362
|
+
by_noid = nodes.index_by { |n| n["noid"] }
|
|
363
|
+
by_noid["col-456"].title # => "Some Collection"
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Each entry is a lightweight digest — `{ "id", "noid", "klass", "title",
|
|
367
|
+
"thumbnail", "tombstoned" }` — not the full typed payload. The ids travel
|
|
368
|
+
in the request body (no URL-length ceiling). The result is **unordered**
|
|
369
|
+
and **may be shorter than the input**: unresolvable ids are dropped and
|
|
370
|
+
tombstoned resources come back flagged (`"tombstoned" => true`), so index
|
|
371
|
+
by `"noid"` rather than assuming positional correspondence. NOIDs only —
|
|
372
|
+
raw Valkyrie ids are not a supported input.
|
|
373
|
+
|
|
315
374
|
## End-to-end example
|
|
316
375
|
|
|
317
376
|
JSON responses come back as `AtlasRb::Mash` (a `Hashie::Mash` subclass), so
|
data/lib/atlas_rb/resource.rb
CHANGED
|
@@ -47,6 +47,44 @@ module AtlasRb
|
|
|
47
47
|
"resource" => result.first[1])
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
# Resolve many resources by NOID in a single round-trip.
|
|
51
|
+
#
|
|
52
|
+
# Wraps Atlas's `POST /resources/find_many`, which returns one lightweight
|
|
53
|
+
# digest per resolvable resource — `{ "id", "noid", "klass", "title",
|
|
54
|
+
# "thumbnail", "tombstoned" }` — rather than full typed payloads. Use it
|
|
55
|
+
# anywhere a set of ids would otherwise be resolved with a `find`-per-id
|
|
56
|
+
# fan-out (breadcrumb chains, linked-member lists, load-destination
|
|
57
|
+
# pickers): one HTTP call instead of N.
|
|
58
|
+
#
|
|
59
|
+
# The ids travel in the request **body**, so the list is not bounded by
|
|
60
|
+
# URL length. The result is **unordered** and **may be shorter than the
|
|
61
|
+
# input** — unresolvable ids are dropped silently, and tombstoned
|
|
62
|
+
# resources come back flagged (`"tombstoned" => true`) rather than
|
|
63
|
+
# omitted. Index the result by `"noid"`; do not assume positional
|
|
64
|
+
# correspondence with `ids`.
|
|
65
|
+
#
|
|
66
|
+
# @param ids [Array<String>] resource NOIDs to resolve. (Raw Valkyrie ids
|
|
67
|
+
# are not a supported input — the endpoint resolves alternate ids only.)
|
|
68
|
+
# @param nuid [String, nil] optional acting user's NUID, forwarded as the
|
|
69
|
+
# `User:` header. Required for cerberus-token requests; legacy bearer
|
|
70
|
+
# tokens still resolve without it.
|
|
71
|
+
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
72
|
+
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
73
|
+
# omitted.
|
|
74
|
+
# @return [Array<AtlasRb::Mash>] one digest Mash per resolved resource
|
|
75
|
+
# (dot- or string-keyed access); empty when nothing resolved.
|
|
76
|
+
#
|
|
77
|
+
# @example Resolve a set of collection titles in one call
|
|
78
|
+
# nodes = AtlasRb::Resource.find_many(["col-456", "col-457", "missing"])
|
|
79
|
+
# by_noid = nodes.index_by { |n| n["noid"] }
|
|
80
|
+
# by_noid["col-456"].title # => "Some Collection"
|
|
81
|
+
def self.find_many(ids, nuid: nil, on_behalf_of: nil)
|
|
82
|
+
JSON.parse(
|
|
83
|
+
connection({}, nuid, on_behalf_of: on_behalf_of)
|
|
84
|
+
.post('/resources/find_many', JSON.dump(ids: Array(ids)))&.body
|
|
85
|
+
).map { |node| AtlasRb::Mash.new(node) }
|
|
86
|
+
end
|
|
87
|
+
|
|
50
88
|
# Validate a MODS XML document against Atlas's schema *without* persisting it.
|
|
51
89
|
#
|
|
52
90
|
# Useful for surfacing validation errors in UIs before the user commits.
|
|
@@ -128,5 +166,81 @@ module AtlasRb
|
|
|
128
166
|
.get('/resources/' + id + '/history')&.body
|
|
129
167
|
))
|
|
130
168
|
end
|
|
169
|
+
|
|
170
|
+
# List the retained MODS versions for a resource.
|
|
171
|
+
#
|
|
172
|
+
# Wraps Atlas's `GET /resources/<id>/mods/versions`, which returns the
|
|
173
|
+
# full envelope — `resource_id` plus a reverse-chronological `versions`
|
|
174
|
+
# array — as an `AtlasRb::Mash`. Each version descriptor mirrors the
|
|
175
|
+
# audit-event shape (`version_id`, `created`, `actor_nuid`,
|
|
176
|
+
# `on_behalf_of_nuid`, `source`, `note`) so the two streams render with
|
|
177
|
+
# the same helpers; actor fields are correlated from the audit log and
|
|
178
|
+
# may be `null` when a version has no matching edit event.
|
|
179
|
+
#
|
|
180
|
+
# Type-agnostic: pass any Modsable resource ID (Community, Collection,
|
|
181
|
+
# Work). A resource with no MODS comes back as `{ "versions" => [] }`.
|
|
182
|
+
#
|
|
183
|
+
# Version labels are opaque, sortable OCFL `vN` strings — not a 1-based
|
|
184
|
+
# counter — so treat them as identifiers to feed back into
|
|
185
|
+
# {mods_version}, not as ordinals. The server admin-gates this endpoint
|
|
186
|
+
# (it exposes edit attribution); `401` / `403` surface as raw Faraday
|
|
187
|
+
# responses, matching {history}.
|
|
188
|
+
#
|
|
189
|
+
# @param id [String] an Atlas resource ID.
|
|
190
|
+
# @param nuid [String, nil] optional acting user's NUID, forwarded as the
|
|
191
|
+
# `User:` header. Required for cerberus-token requests; legacy bearer
|
|
192
|
+
# tokens still resolve without it.
|
|
193
|
+
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
194
|
+
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
195
|
+
# omitted.
|
|
196
|
+
# @return [AtlasRb::Mash] the parsed envelope, with `"resource_id"` and a
|
|
197
|
+
# `"versions"` array (reverse chronological; possibly empty).
|
|
198
|
+
#
|
|
199
|
+
# @example
|
|
200
|
+
# history = AtlasRb::Resource.mods_versions("w-789")
|
|
201
|
+
# history["versions"].first["version_id"] # => "v5"
|
|
202
|
+
# history["versions"].first["actor_nuid"] # => "000000002"
|
|
203
|
+
def self.mods_versions(id, nuid: nil, on_behalf_of: nil)
|
|
204
|
+
AtlasRb::Mash.new(JSON.parse(
|
|
205
|
+
connection({}, nuid, on_behalf_of: on_behalf_of)
|
|
206
|
+
.get('/resources/' + id + '/mods/versions')&.body
|
|
207
|
+
))
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Fetch the MODS document as of a specific version.
|
|
211
|
+
#
|
|
212
|
+
# Wraps Atlas's `GET /resources/<id>/mods/versions/<version_id>` and
|
|
213
|
+
# returns the **raw response body** (not parsed) — mirroring
|
|
214
|
+
# {Work.mods}. Pass a `version_id` obtained from {mods_versions} (an
|
|
215
|
+
# opaque OCFL `vN` label).
|
|
216
|
+
#
|
|
217
|
+
# Only XML is version-recoverable: the JSON access copy is overwritten in
|
|
218
|
+
# place, so the server serves historical XML (the default). `kind:` is
|
|
219
|
+
# accepted for parity with {Work.mods} but XML is currently the only
|
|
220
|
+
# supported format. An unknown version yields a `404` (raw Faraday
|
|
221
|
+
# response).
|
|
222
|
+
#
|
|
223
|
+
# @param id [String] an Atlas resource ID.
|
|
224
|
+
# @param version_id [String] an OCFL version label from {mods_versions},
|
|
225
|
+
# e.g. `"v3"`.
|
|
226
|
+
# @param kind [String, nil] response format extension. Omit (or pass
|
|
227
|
+
# `"xml"`) for the historical XML — the only format the server retains.
|
|
228
|
+
# @param nuid [String, nil] optional acting user's NUID, forwarded as the
|
|
229
|
+
# `User:` header. Required for cerberus-token requests; legacy bearer
|
|
230
|
+
# tokens still resolve without it.
|
|
231
|
+
# @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
|
|
232
|
+
# header. Falls through to {AtlasRb.config}.default_on_behalf_of when
|
|
233
|
+
# omitted.
|
|
234
|
+
# @return [String] the raw MODS XML body for that version.
|
|
235
|
+
#
|
|
236
|
+
# @example Diff two MODS versions
|
|
237
|
+
# old_xml = AtlasRb::Resource.mods_version("w-789", "v3")
|
|
238
|
+
# new_xml = AtlasRb::Resource.mods_version("w-789", "v5")
|
|
239
|
+
def self.mods_version(id, version_id, kind: nil, nuid: nil, on_behalf_of: nil)
|
|
240
|
+
connection({}, nuid, on_behalf_of: on_behalf_of).get(
|
|
241
|
+
'/resources/' + id + '/mods/versions/' + version_id +
|
|
242
|
+
(kind.present? ? ".#{kind}" : '')
|
|
243
|
+
)&.body
|
|
244
|
+
end
|
|
131
245
|
end
|
|
132
246
|
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.3.1
|
|
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-06-
|
|
11
|
+
date: 2026-06-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|