atlas_rb 1.21.0 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0650d39388fca9d7fbdc0134ee4e76e4a82ed3c659f8c44ea96aac62a397d0c1
4
- data.tar.gz: ab674da7f6274991b11921b60c43a93ea6e3e9f3a5ef121109fd3bc99c970cdc
3
+ metadata.gz: 8df0d572649e0456aa2a8e77b436114aa3a91a2fbfe0e6c05ce2a8426dd7c6ff
4
+ data.tar.gz: e368597905337f62111f4e649eb88a75f5e06c85202ff3dcf5cc3e99d283d948
5
5
  SHA512:
6
- metadata.gz: 2c23136adc3f95ccf1b9e0e739f95a2a86b3ee33a1cf5a52f72af625fe5c2ab77cf63c9c78990d2b36ff0d285445f56bae620da21d86af0994d590f867defe51
7
- data.tar.gz: edac22a77b8d592ae2e3a0d77117b9ac60d7516d3a13469747ff5cb7f90a782e774d5514218bb5d368818defc538d58158ed608abd400d2cca4aaa606bff9939
6
+ metadata.gz: 005ae8ac014dab468bbfb9c5da901c926ba2c746d78c3bedadda431e310d3d21b99f547b91f8e33563784ba45a782cefa4155c7fcfc35a09c11c8e9f43d9fe5b
7
+ data.tar.gz: 2d35888d562851ec0ad7cd9419faceee619568723f98c1a34e172ac01f33c56e2ae8491eedae2d851067969acfdd02bf6439364d3b2517448cfadb0a13436fa9
data/.version CHANGED
@@ -1 +1 @@
1
- 1.21.0
1
+ 1.23.0
data/CHANGELOG.md CHANGED
@@ -1,5 +1,69 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.23.0
4
+
5
+ These bindings need Atlas 0.6.212 or later.
6
+
7
+ ### Added — `Work.withdrawn_assets`, the listing a Restore needs
8
+
9
+ ```ruby
10
+ AtlasRb::Work.withdrawn_assets(work_id, nuid: admin_nuid).each do |asset|
11
+ AtlasRb::Admin::Resource.restore(asset.file_set, nuid: admin_nuid)
12
+ end
13
+ ```
14
+
15
+ A FileSet withdrawn with `Resource.tombstone` drops out of `Work.assets` and
16
+ `Work.file_sets`, so nothing listed it. This read lists the assets of a Work's
17
+ withdrawn FileSets, in the `Work.assets` shape plus the FileSet's
18
+ `tombstoned_at` and `tombstoned_by`. Atlas allows it for the admin and
19
+ devolved-admin tiers; anyone else gets a `403`, which raises `ResourceError`. A
20
+ missing Work returns `nil`.
21
+
22
+ ### Documented — `file_set` on every `Work.assets` and `Work.file_sets` entry
23
+
24
+ Each asset entry carries the NOID of the FileSet it is listed under. That is
25
+ the id `Resource.tombstone` and `Admin::Resource.restore` take, so a caller no
26
+ longer needs `Blob.ancestry` to find it. No reader change.
27
+
28
+ ## 1.22.0
29
+
30
+ These bindings need Atlas 0.6.211 or later.
31
+
32
+ ### Added — `Blob.update(original_filename:)`, renaming the file on a replace
33
+
34
+ ```ruby
35
+ AtlasRb::Blob.update(blob_id, "/tmp/upload.tmp", original_filename: "report.pdf")
36
+ ```
37
+
38
+ A replacement of a different type used to keep the old name, so a `.docx`
39
+ replaced by a `.pdf` downloaded as `.docx`. Atlas now records the name for the
40
+ new revision and re-derives the MIME type from it. For the original file it also
41
+ re-derives the label and the FileSet's classification. Omit the keyword to keep
42
+ the current name. `Blob.rollback` needs no change: Atlas restores the revision's
43
+ own name.
44
+
45
+ ### Added — `language:` and `track_label:` on `Blob.create` and `Blob.update`
46
+
47
+ ```ruby
48
+ AtlasRb::Blob.create(work_id, "/tmp/es.vtt", "es.vtt", language: "es", track_label: "Español")
49
+ ```
50
+
51
+ A caption's BCP 47 language and the name a player shows for it. Each is sent
52
+ only when given, so an update keeps a value it does not mention, and `""`
53
+ clears one. Atlas refuses a malformed value with a `422`, which raises
54
+ `ResourceError`. `Work.assets` and `Work.file_sets` return both fields on Blob
55
+ entries with no reader change.
56
+
57
+ ### Documented — tombstoning a FileSet, and the `original` image tier
58
+
59
+ - `Resource.tombstone` and `Admin::Resource.restore` accept a FileSet id. That
60
+ withdraws a caption or other attached file reversibly. Atlas allows it for the
61
+ admin and devolved-admin tiers only. Despite its namespace,
62
+ `Admin::Resource.restore` is called by delegates too.
63
+ - `Work.set_derivative_permissions` documents the image floor as `original`, not
64
+ `master`. Atlas accepts `master` on write for one release and stores it as
65
+ `original`.
66
+
3
67
  ## 1.21.0
4
68
 
5
69
  ### Added — `System::Work.remove_linked_member`, unlinking a showcase Work
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- atlas_rb (1.21.0)
4
+ atlas_rb (1.23.0)
5
5
  faraday (~> 2.7)
6
6
  faraday-follow_redirects (~> 0.3.0)
7
7
  faraday-multipart (~> 1)
@@ -53,6 +53,13 @@ module AtlasRb
53
53
  # stub. No `confirm:` marker — restoring is itself reversible, by
54
54
  # tombstoning again.
55
55
  #
56
+ # Accepts a FileSet id too (Atlas 0.6.211 or later), which puts a withdrawn
57
+ # caption or other attached file back in {Work.assets}.
58
+ #
59
+ # The devolved-admin tier calls this as well, although the namespace reads
60
+ # as admin-only. Atlas owns the gate; this namespace only marks the call as
61
+ # an operator action.
62
+ #
56
63
  # @param id [String] the resource's NOID.
57
64
  # @param nuid [String, nil] optional acting user's NUID.
58
65
  # @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
data/lib/atlas_rb/blob.rb CHANGED
@@ -144,6 +144,12 @@ module AtlasRb
144
144
  # @param blob_path [String] path to the binary file on disk to upload.
145
145
  # @param original_filename [String] the user-facing filename Atlas
146
146
  # should record (e.g. `"final_thesis.pdf"`).
147
+ # @param language [String, nil] optional BCP 47 language of a caption or
148
+ # other text track (e.g. `"en"`, `"es-MX"`). Atlas checks the shape of the
149
+ # tag, not the registry, and refuses a malformed one with `422`
150
+ # `invalid_language` before storing anything. Needs Atlas 0.6.211 or later.
151
+ # @param track_label [String, nil] optional name a player shows for the track
152
+ # (e.g. `"Español"`), at most 64 characters (`422 invalid_track_label`).
147
153
  # @param idempotency_key [String, nil] optional UUID. A repeat call with
148
154
  # the same key returns the originally-created Blob instead of creating
149
155
  # a new one. See {AtlasRb::Work.create} for full semantics.
@@ -178,11 +184,15 @@ module AtlasRb
178
184
  # key = SecureRandom.uuid
179
185
  # AtlasRb::Blob.create("w-789", "/tmp/upload.tmp", "thesis.pdf",
180
186
  # idempotency_key: key, expected_digest: "sha256:#{sha}")
181
- def self.create(id, blob_path, original_filename, expected_digest: nil,
187
+ #
188
+ # @example A Spanish caption track
189
+ # AtlasRb::Blob.create("w-789", "/tmp/es.vtt", "es.vtt", language: "es", track_label: "Español")
190
+ def self.create(id, blob_path, original_filename, expected_digest: nil, language: nil, track_label: nil,
182
191
  idempotency_key: nil, nuid: nil, on_behalf_of: nil)
183
192
  with_file_part(blob_path) do |part|
193
+ # compact drops only nil, so an empty string still reaches Atlas.
184
194
  payload = { work_id: id, original_filename: original_filename, binary: part }
185
- payload[:expected_digest] = expected_digest if expected_digest
195
+ .merge({ expected_digest: expected_digest, language: language, track_label: track_label }.compact)
186
196
 
187
197
  AtlasRb::Mash.new(write_resource(
188
198
  multipart(nuid, on_behalf_of: on_behalf_of, idempotency_key: idempotency_key)
@@ -215,12 +225,22 @@ module AtlasRb
215
225
 
216
226
  # Replace the bytes of an existing Blob in-place.
217
227
  #
218
- # The Blob ID is preserved; only the underlying content changes. The
219
- # original filename is *not* updated by this call — use a new
220
- # {.create} if you need a different `original_filename`.
228
+ # The Blob ID is preserved and a new revision is appended. Pass
229
+ # `original_filename` when the replacement is a different file, above all a
230
+ # different type: Atlas records the name for this revision, and re-derives
231
+ # the MIME type from it. For the original file it also re-derives the label
232
+ # and the FileSet's classification; a derivative keeps its tier label.
233
+ # {.rollback} restores a revision's own name, so it needs no name.
221
234
  #
222
235
  # @param id [String] the Blob ID.
223
236
  # @param blob_path [String] path to the replacement binary on disk.
237
+ # @param original_filename [String, nil] the replacement's own filename.
238
+ # Omit it to keep the current name. Needs Atlas 0.6.211 or later; an older
239
+ # Atlas ignores it and keeps the name.
240
+ # @param language [String, nil] a new BCP 47 language for the track. Omit it
241
+ # to keep the current one; pass `""` to clear it. See {.create}.
242
+ # @param track_label [String, nil] a new display name for the track. Omit it
243
+ # to keep the current one; pass `""` to clear it.
224
244
  # @param expected_digest [String, nil] optional verify-on-ingest checksum,
225
245
  # `"<algorithm>:<hexvalue>"`. 422 ({AtlasRb::FixityMismatchError}) on mismatch.
226
246
  # @param idempotency_key [String, nil] optional UUID. A double-submit of the
@@ -249,10 +269,15 @@ module AtlasRb
249
269
  #
250
270
  # @example Retry-safe replace
251
271
  # AtlasRb::Blob.update("b-321", "/tmp/revised.pdf", idempotency_key: SecureRandom.uuid)
252
- def self.update(id, blob_path, expected_digest: nil, idempotency_key: nil, nuid: nil, on_behalf_of: nil)
272
+ #
273
+ # @example Replace a Word file with a PDF
274
+ # AtlasRb::Blob.update("b-321", "/tmp/upload.tmp", original_filename: "report.pdf")
275
+ def self.update(id, blob_path, original_filename: nil, expected_digest: nil, language: nil, track_label: nil,
276
+ idempotency_key: nil, nuid: nil, on_behalf_of: nil)
253
277
  with_file_part(blob_path) do |part|
254
- payload = { binary: part }
255
- payload[:expected_digest] = expected_digest if expected_digest
278
+ # compact drops only nil, so `""` still reaches Atlas and clears a field.
279
+ payload = { binary: part, original_filename: original_filename, expected_digest: expected_digest,
280
+ language: language, track_label: track_label }.compact
256
281
 
257
282
  AtlasRb::Mash.new(write_resource(
258
283
  multipart(nuid, on_behalf_of: on_behalf_of, idempotency_key: idempotency_key)
@@ -268,7 +293,8 @@ module AtlasRb
268
293
  # envelope: one descriptor per retained content revision, each carrying its
269
294
  # OCFL `version_id` label, the `file_identifier` appended for that revision,
270
295
  # the `created` timestamp, the `digest`/`size` recorded at that version,
271
- # the stable `original_filename`, and actor attribution (`actor_nuid` /
296
+ # that revision's own `original_filename` (a replace can rename the file),
297
+ # and actor attribution (`actor_nuid` /
272
298
  # `on_behalf_of_nuid`, null when no audit event correlates).
273
299
  #
274
300
  # Server admin-gates this endpoint (it exposes edit attribution), so
@@ -138,6 +138,12 @@ module AtlasRb
138
138
  # `has_live_children` — a legitimate answer the caller has to read, not an
139
139
  # error. Reversible via {.restore}.
140
140
  #
141
+ # Accepts a FileSet id as well as a Community, Collection or Work (Atlas
142
+ # 0.6.211 or later). That withdraws an attached file, such as a caption,
143
+ # reversibly: its Blobs ride along and it drops out of {Work.assets} and
144
+ # {Work.file_sets}. Atlas refuses a FileSet from anyone outside the admin and
145
+ # devolved-admin tiers with `403`, even a user who can edit the Work.
146
+ #
141
147
  # @param id [String] the resource's NOID.
142
148
  # @param nuid [String, nil] the acting user's NUID, stamped on the resource
143
149
  # as `tombstoned_by`.
data/lib/atlas_rb/work.rb CHANGED
@@ -328,7 +328,7 @@ module AtlasRb
328
328
  # Grouper group names, `[]` = private). Two media families:
329
329
  #
330
330
  # * the image ladder `small` / `medium` / `large` / `service` (deep-zoom) /
331
- # `master` (the original image), and
331
+ # `original` (the deposited image), and
332
332
  # * independent media `audio` / `video` / `pdf`.
333
333
  #
334
334
  # Unlike {.set_image_derivatives} (which upserts URIs) this is a whole-object
@@ -340,18 +340,20 @@ module AtlasRb
340
340
  #
341
341
  # Atlas enforces: a tier may not be more visible than the Work, and — within
342
342
  # the image ladder — visibility must narrow as resolution grows
343
- # (`master` ⊆ `service` ⊆ `large` ⊆ `medium` ⊆ `small`; independent media
343
+ # (`original` ⊆ `service` ⊆ `large` ⊆ `medium` ⊆ `small`; independent media
344
344
  # impose no ordering). The gate is advisory — it surfaces on {.assets} as
345
- # `gated` / `permission` for BOTH Delegate (image tier) and Blob (master /
345
+ # `gated` / `permission` for BOTH Delegate (image tier) and Blob (original /
346
346
  # pdf / audio / video, classified by media type) entries, for the display
347
347
  # layer (Cerberus / the IIIF auth service; Cerberus's download :read check)
348
348
  # to enforce.
349
349
  #
350
350
  # @param id [String] the Work ID.
351
351
  # @param policy [Hash] tier => Array(read groups), e.g.
352
- # `{ large: ["northeastern:drs:repository:archives"], master: [...] }`.
352
+ # `{ large: ["northeastern:drs:repository:archives"], original: [...] }`.
353
353
  # Keys may be strings or symbols; recognized keys are `small` / `medium` /
354
- # `large` / `service` / `master` / `audio` / `video` / `pdf`.
354
+ # `large` / `service` / `original` / `audio` / `video` / `pdf`. Atlas
355
+ # 0.6.211 renamed the image floor from `master`; for one release it still
356
+ # accepts `master` on write and stores it as `original`.
355
357
  # @param nuid [String, nil] optional acting user's NUID. On the relay-signing
356
358
  # path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
357
359
  # path it is ignored (identity lives in the token).
@@ -435,6 +437,15 @@ module AtlasRb
435
437
  # authorized rather than fetched directly) and `permission` (the effective
436
438
  # read-group set, or `nil` for guests, to whom group names are withheld).
437
439
  #
440
+ # A Blob entry also carries `language` and `track_label`, set by {Blob.create}
441
+ # and {Blob.update} for a caption or other text track and `nil` otherwise
442
+ # (Atlas 0.6.211 or later). A FileSet withdrawn with {Resource.tombstone}
443
+ # drops out of this listing and {.file_sets}; {.withdrawn_assets} lists it.
444
+ #
445
+ # Every entry carries `file_set`, the NOID of the FileSet it is listed
446
+ # under (Atlas 0.6.212 or later). That is the id {Resource.tombstone} and
447
+ # {Admin::Resource.restore} take to withdraw or restore the file.
448
+ #
438
449
  # @param id [String] the Work ID.
439
450
  # @param nuid [String, nil] optional acting user's NUID. On the relay-signing
440
451
  # path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
@@ -458,6 +469,44 @@ module AtlasRb
458
469
  end
459
470
  end
460
471
 
472
+ # List the assets of a Work's withdrawn FileSets, so they can be restored.
473
+ #
474
+ # Wraps `GET /works/<id>/withdrawn_assets` (Atlas 0.6.212 or later). A
475
+ # FileSet withdrawn with {Resource.tombstone} drops out of {.assets} and
476
+ # {.file_sets}; this is the listing that still names it. Each entry has the
477
+ # {.assets} shape, including `file_set` (the id to pass to
478
+ # {Admin::Resource.restore}), plus the FileSet's `tombstoned_at` and
479
+ # `tombstoned_by`.
480
+ #
481
+ # Atlas allows it for the admin and devolved-admin tiers only, the same
482
+ # tiers that may tombstone and restore a FileSet.
483
+ #
484
+ # @param id [String] the Work ID.
485
+ # @param nuid [String, nil] optional acting user's NUID. On the relay-signing
486
+ # path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
487
+ # path it is ignored (identity lives in the token).
488
+ # @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
489
+ # header. Falls through to {AtlasRb.config}.default_on_behalf_of when
490
+ # omitted.
491
+ # @return [Array<AtlasRb::Mash>, nil] one entry per asset of a withdrawn
492
+ # FileSet; `[]` when nothing is withdrawn.
493
+ #
494
+ # `nil` when Atlas answers `404` — nothing is there to read, or, with a
495
+ # misconfigured `ATLAS_URL`, the route is not Atlas's at all.
496
+ # @raise [AtlasRb::ResourceError] on any non-2xx other than `404` / `410`
497
+ # (a `403` for a caller below the delegate tier, a `5xx`, a proxy's
498
+ # `503`), carrying Atlas's status and body so the failure is attributable
499
+ # at the boundary.
500
+ # @example Offer Restore for a removed caption
501
+ # AtlasRb::Work.withdrawn_assets("w-789", nuid: admin_nuid).each do |a|
502
+ # AtlasRb::Admin::Resource.restore(a.file_set, nuid: admin_nuid)
503
+ # end
504
+ def self.withdrawn_assets(id, nuid: nil, on_behalf_of: nil)
505
+ read_body(connection({}, nuid, on_behalf_of: on_behalf_of).get(ROUTE + id + '/withdrawn_assets')) do |body|
506
+ body.map { |entry| AtlasRb::Mash.new(entry) }
507
+ end
508
+ end
509
+
461
510
  # List a Work's page FileSets in order, each with its assets.
462
511
  #
463
512
  # Wraps `GET /works/<id>/file_sets` — the ordered, grouped sibling of
@@ -466,7 +515,7 @@ module AtlasRb
466
515
  # (`null`-position) FileSets last; metadata and derivative-container
467
516
  # FileSets are excluded as entries. Each entry nests its downloadable
468
517
  # assets — the page's content Blobs plus any per-page IIIF Delegates —
469
- # in the same polymorphic shape {.assets} returns.
518
+ # in the same polymorphic shape {.assets} returns, `file_set` included.
470
519
  #
471
520
  # This is the read a IIIF Presentation manifest assembler needs: the
472
521
  # response is **unpaginated** by design, so the whole page sequence
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.21.0
4
+ version: 1.23.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-09-29 00:00:00.000000000 Z
11
+ date: 2026-09-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday