atlas_rb 1.10.0 → 1.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 568ae8b7b6c4f459c528c79d358e5969333b0d35201c5b5f8342931bff232d00
4
- data.tar.gz: bbfa0f0f70bafc15c5fb402ce3051795250716e3229b8cb3b70e9fec5b62af19
3
+ metadata.gz: 6d8edc13167cb16fb5482d6a5cd19760bf323f67bf1a23f00a9508f8320bf798
4
+ data.tar.gz: e86459ce460b8eb30cffcfaaed75b06c750649aa57559e33d5ea277ce0bd2d8c
5
5
  SHA512:
6
- metadata.gz: 22976962a78e3aa7c53253db913daa0306a27c8d378490443a774acaeac6afb83d6ec7b0da15942ad9c276dd7dd454ce9ccae26880189b61fa94dfbdf8977fc9
7
- data.tar.gz: 634edc0528a3872d201b67506030d0ed88d5956da985336ba9cebf7b78cc49a997a5806dc2b51ec2071774b499ac2f4c1f2235f25dfc784a1d04e7cf0c9b9123
6
+ metadata.gz: 06a66bf273c655ae075c821d158555139094714e3b5d0f3ca15296657afe067267ef781190c4804d5a97d3576237a11d664141f28256702df4151fefde515255
7
+ data.tar.gz: dfd113525542142a75e0789a5e9ed34424485e1ac69359f5e4c505e24d3752e9eda54e756804f13f195588f6079d0c828426e0a5b937d8ad53c2ef8708eaff00
data/.version CHANGED
@@ -1 +1 @@
1
- 1.10.0
1
+ 1.10.1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.10.1
4
+
5
+ ### Added — `Work.mark_incomplete` / `Work.clear_incomplete`
6
+
7
+ A Work can now carry a second lifecycle state. `complete` says the deposit
8
+ finished; `incomplete` says something downstream of it gave up. Before this
9
+ there was no way to record that at all: every give-up handler wrote a
10
+ `Rails.logger.warn` and nothing else, so a deposit whose PDF rendition
11
+ exhausted its retries ended up with no rendition and no thumbnail, the
12
+ depositor was not told, and no surface listed it.
13
+
14
+ ```ruby
15
+ AtlasRb::Work.mark_incomplete(work_id, reason: "pdf_rendition_gave_up")
16
+ AtlasRb::Work.clear_incomplete(work_id)
17
+ ```
18
+
19
+ `reason` is a machine token, one per give-up handler. Atlas holds it as an
20
+ opaque string and does **not** validate it against a list, so the vocabulary
21
+ belongs to the caller and a new token needs no Atlas release. Map it to display
22
+ text at the point of use, with a fallback for a token the view has not been
23
+ taught.
24
+
25
+ The flag **never hides** the Work. A record with its file, title and metadata
26
+ but one missing derivative is degraded, not broken, and stays readable.
27
+
28
+ Both bindings return the updated Work. `mark_incomplete` is idempotent and the
29
+ last reason wins; `clear_incomplete` is idempotent too, and clears the flag and
30
+ the reason together. Call it when a later run of the same job succeeds, which
31
+ makes the state self-healing.
32
+
33
+ ### Added — `incomplete:` filter on `Work.list`
34
+
35
+ `AtlasRb::Work.list(incomplete: true)` is the staff list of Works whose
36
+ pipeline gave up, the sibling of the existing `in_progress:` "what's stuck?"
37
+ view. The two are independent and combine: `in_progress: false, incomplete:
38
+ true` reads as "finished, but degraded".
39
+
40
+ Work summaries in the response now carry `incomplete` and `incomplete_reason`
41
+ alongside `in_progress`.
42
+
3
43
  ## 1.10.0
4
44
 
5
45
  ### Removed — `title` on `Person.create` and `Person.update`
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- atlas_rb (1.10.0)
4
+ atlas_rb (1.10.1)
5
5
  faraday (~> 2.7)
6
6
  faraday-follow_redirects (~> 0.3.0)
7
7
  faraday-multipart (~> 1)
data/README.md CHANGED
@@ -255,6 +255,28 @@ AtlasRb::Work.list(in_progress: false, page: 2) # completed deposits, page 2
255
255
  AtlasRb::Work.complete("w-789") # mark w-789 done
256
256
  ```
257
257
 
258
+ A Work carries a second, independent lifecycle state for the other kind of
259
+ failure. `complete` says the deposit finished; `incomplete` says something
260
+ downstream of it gave up — a rendition, the derivatives, the full-text
261
+ extraction — after that job exhausted its retries. Call `mark_incomplete`
262
+ from the give-up handler, and `clear_incomplete` when a later run succeeds,
263
+ which makes the state self-healing.
264
+
265
+ ```ruby
266
+ AtlasRb::Work.mark_incomplete("w-789", reason: "pdf_rendition_gave_up")
267
+ AtlasRb::Work.clear_incomplete("w-789")
268
+ AtlasRb::Work.list(incomplete: true) # the staff repair list
269
+ AtlasRb::Work.list(in_progress: false, incomplete: true) # finished, but degraded
270
+ ```
271
+
272
+ `reason` is a machine token, one per give-up handler. Atlas stores it as an
273
+ opaque string and does not validate it against a list, so the vocabulary is
274
+ yours and a new token needs no Atlas release. Map it to display text at the
275
+ point of use, with a fallback for a token the view has not been taught.
276
+
277
+ The flag never hides the Work. A record with its file, title and metadata but
278
+ one missing derivative is degraded, not broken, and stays readable.
279
+
258
280
  ### Audit-event history
259
281
 
260
282
  `Resource.history` wraps Atlas's `GET /resources/<id>/history` endpoint
data/lib/atlas_rb/work.rb CHANGED
@@ -43,6 +43,11 @@ module AtlasRb
43
43
  #
44
44
  # @param in_progress [Boolean, nil] when set, filter to Works whose
45
45
  # `in_progress` flag matches. Omit (or pass `nil`) for "all works".
46
+ # @param incomplete [Boolean, nil] when set, filter to Works whose
47
+ # `incomplete` flag matches — the staff list of Works whose enrichment
48
+ # pipeline gave up (see {.mark_incomplete}). Independent of
49
+ # `in_progress:`, and the two combine: `in_progress: false,
50
+ # incomplete: true` reads as "finished, but degraded".
46
51
  # @param page [Integer, nil] 1-indexed page number.
47
52
  # @param per_page [Integer, nil] page size override.
48
53
  # @param nuid [String, nil] optional acting user's NUID. On the relay-signing
@@ -53,16 +58,20 @@ module AtlasRb
53
58
  # omitted.
54
59
  # @return [AtlasRb::Mash] `{ "works" => [...], "pagination" => {...} }`.
55
60
  # Each entry in `"works"` is a Work summary (`id`, `title`,
56
- # `description`, `in_progress`).
61
+ # `description`, `in_progress`, `incomplete`, `incomplete_reason`).
57
62
  #
58
63
  # @example Find stuck deposits
59
64
  # AtlasRb::Work.list(in_progress: true)
60
65
  #
66
+ # @example Find works whose pipeline gave up
67
+ # AtlasRb::Work.list(incomplete: true)
68
+ #
61
69
  # @example Page through all works
62
70
  # AtlasRb::Work.list(page: 2, per_page: 50)
63
- def self.list(in_progress: nil, page: nil, per_page: nil, nuid: nil, on_behalf_of: nil)
71
+ def self.list(in_progress: nil, incomplete: nil, page: nil, per_page: nil, nuid: nil, on_behalf_of: nil)
64
72
  params = {}
65
73
  params[:in_progress] = in_progress unless in_progress.nil?
74
+ params[:incomplete] = incomplete unless incomplete.nil?
66
75
  params[:page] = page if page
67
76
  params[:per_page] = per_page if per_page
68
77
  AtlasRb::Mash.new(JSON.parse(
@@ -232,6 +241,88 @@ module AtlasRb
232
241
  connection({}, nuid, on_behalf_of: on_behalf_of).post(ROUTE + id + '/complete')
233
242
  end
234
243
 
244
+ # Flag a Work whose enrichment pipeline gave up.
245
+ #
246
+ # The counterpart to {.complete}, for the other half of the lifecycle:
247
+ # `complete` says the deposit finished, this says something downstream of
248
+ # it did not. Call it from a work-scoped job's give-up handler — the PDF or
249
+ # media rendition, the derivatives, the full-text extraction — once that
250
+ # job has exhausted its retries. Atlas's `GET /works?incomplete=true` then
251
+ # lists the Work for staff, and `incomplete_bsi` on its Solr document lets
252
+ # a result row render a pill without a per-row fetch.
253
+ #
254
+ # The flag **never hides** the Work. A record with its file, title and
255
+ # metadata but one missing derivative is degraded, not broken, and stays
256
+ # readable — enrichment does not fail a deposit.
257
+ #
258
+ # Idempotent on the server; the last reason wins. Clear it with
259
+ # {.clear_incomplete} when a later run of the same job succeeds, which
260
+ # makes the state self-healing.
261
+ #
262
+ # @param id [String] the Work ID.
263
+ # @param reason [String, nil] a machine token naming the cause — one per
264
+ # give-up handler, e.g. `"pdf_rendition_gave_up"`,
265
+ # `"media_rendition_gave_up"`, `"ingest_gave_up"`. Atlas stores it as an
266
+ # opaque string and does **not** validate it against a list, so the
267
+ # vocabulary is the caller's and a new token needs no Atlas release. Map
268
+ # it to display text at the point of use, with a fallback for a token the
269
+ # view has not been taught. A blank reason still sets the flag.
270
+ # @param nuid [String, nil] optional NUID of the acting user.
271
+ # @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
272
+ # header. Falls through to {AtlasRb.config}.default_on_behalf_of when
273
+ # omitted.
274
+ # @return [Hash] the updated Work, the same shape {.find} returns, carrying
275
+ # `incomplete` and `incomplete_reason`.
276
+ # @raise [AtlasRb::StaleResourceError] if Atlas reports an optimistic-lock
277
+ # conflict that exhausted its internal retry budget (HTTP 409 with
278
+ # `error: "stale_resource"`).
279
+ # @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
280
+ # resource, so the write did not happen.
281
+ # @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
282
+ # and body.
283
+ #
284
+ # @example In a give-up handler
285
+ # AtlasRb::Work.mark_incomplete(work_id, reason: "pdf_rendition_gave_up")
286
+ def self.mark_incomplete(id, reason:, nuid: nil, on_behalf_of: nil)
287
+ AtlasRb::Mash.new(write_resource(
288
+ connection({}, nuid, on_behalf_of: on_behalf_of)
289
+ .post(ROUTE + id + '/incomplete', JSON.dump(reason: reason))
290
+ ))["work"]
291
+ end
292
+
293
+ # Clear the incomplete flag and its reason.
294
+ #
295
+ # The repair half of {.mark_incomplete}: call it from the same job when a
296
+ # later run succeeds, or by hand once an operator has fixed the Work. Both
297
+ # fields clear together — a reason without a flag would leave a stale cause
298
+ # on the Solr document.
299
+ #
300
+ # Idempotent: clearing a Work that was never flagged is a no-op.
301
+ #
302
+ # @param id [String] the Work ID.
303
+ # @param nuid [String, nil] optional NUID of the acting user.
304
+ # @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
305
+ # header. Falls through to {AtlasRb.config}.default_on_behalf_of when
306
+ # omitted.
307
+ # @return [Hash] the updated Work, the same shape {.find} returns, with
308
+ # `incomplete` false and `incomplete_reason` null.
309
+ # @raise [AtlasRb::StaleResourceError] if Atlas reports an optimistic-lock
310
+ # conflict that exhausted its internal retry budget (HTTP 409 with
311
+ # `error: "stale_resource"`).
312
+ # @raise [AtlasRb::NotFoundError] if Atlas answers `404` — the id names no such
313
+ # resource, so the write did not happen.
314
+ # @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
315
+ # and body.
316
+ #
317
+ # @example On a later successful run
318
+ # AtlasRb::Work.clear_incomplete(work_id)
319
+ def self.clear_incomplete(id, nuid: nil, on_behalf_of: nil)
320
+ AtlasRb::Mash.new(write_resource(
321
+ connection({}, nuid, on_behalf_of: on_behalf_of)
322
+ .delete(ROUTE + id + '/incomplete')
323
+ ))["work"]
324
+ end
325
+
235
326
  # Replace a Work's metadata by uploading a MODS XML document.
236
327
  #
237
328
  # @param id [String] the Work ID.
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.10.0
4
+ version: 1.10.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-08-04 00:00:00.000000000 Z
11
+ date: 2026-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday