atlas_rb 1.9.4 → 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: 808bf4695fcba808f36a2c808885529bdff8b8dae50ddfed71cd605115aa9014
4
- data.tar.gz: 532e5bac353cceeff1806829f4e8d8f2421051799b3a249bf3119c4001c144fc
3
+ metadata.gz: 6d8edc13167cb16fb5482d6a5cd19760bf323f67bf1a23f00a9508f8320bf798
4
+ data.tar.gz: e86459ce460b8eb30cffcfaaed75b06c750649aa57559e33d5ea277ce0bd2d8c
5
5
  SHA512:
6
- metadata.gz: 177bfa1e9c486473285eba7fac243719f08af97ad58999fdb08c9d08725981ba8dce66143943768ff12a8e08d994ca34eb02a791cd567b7f98294732fd461a2a
7
- data.tar.gz: 0eba14aa8d5ac54b4153fd48810c90fb979ac608cc3bf2b0fb68bdb039ed9b4db763d6610839a0656374f684fc9821bde4d3f7ae336f58ac9f894aa4aee2ad81
6
+ metadata.gz: 06a66bf273c655ae075c821d158555139094714e3b5d0f3ca15296657afe067267ef781190c4804d5a97d3576237a11d664141f28256702df4151fefde515255
7
+ data.tar.gz: dfd113525542142a75e0789a5e9ed34424485e1ac69359f5e4c505e24d3752e9eda54e756804f13f195588f6079d0c828426e0a5b937d8ad53c2ef8708eaff00
data/.version CHANGED
@@ -1 +1 @@
1
- 1.9.4
1
+ 1.10.1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,63 @@
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
+
43
+ ## 1.10.0
44
+
45
+ ### Removed — `title` on `Person.create` and `Person.update`
46
+
47
+ Atlas no longer holds a job title on a Person. The attribute claimed
48
+ Blacklight's title namespace, where display and sorting expect the Person's
49
+ name, so Atlas dropped it rather than renamed it: the field was display-only,
50
+ optional, and absent from v1.
51
+
52
+ The parameter therefore had nothing to write to. Atlas ignores an unknown key in
53
+ a write body, so a caller that kept passing `title:` had its value discarded
54
+ with no error and nothing in a log. That is the reason to remove the parameter
55
+ rather than leave it accepted and inert.
56
+
57
+ **Breaking.** `Person.create(…, title: "Professor")` now raises
58
+ `ArgumentError: unknown keyword: :title`. Drop the argument at the call site;
59
+ there is no replacement field.
60
+
3
61
  ## 1.9.4
4
62
 
5
63
  ### Fixed — write bindings parsed the response body without checking the status
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- atlas_rb (1.9.4)
4
+ atlas_rb (1.10.1)
5
5
  faraday (~> 2.7)
6
6
  faraday-follow_redirects (~> 0.3.0)
7
7
  faraday-multipart (~> 1)
@@ -25,7 +25,7 @@ GEM
25
25
  net-http (~> 0.5)
26
26
  hashie (5.1.0)
27
27
  logger
28
- json (2.21.1)
28
+ json (2.21.2)
29
29
  jwt (2.10.3)
30
30
  base64
31
31
  logger (1.7.0)
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
@@ -85,7 +85,6 @@ module AtlasRb
85
85
  # @param display_name [String] authoritative display name.
86
86
  # @param bio [String, nil]
87
87
  # @param orcid [String, nil]
88
- # @param title [String, nil]
89
88
  # @param on_behalf_of [String, nil] acting-as target (the acting principal
90
89
  # itself comes from the ambient AtlasRb.config.default_nuid).
91
90
  # @return [AtlasRb::Mash] the unwrapped `"person"` object.
@@ -93,8 +92,8 @@ module AtlasRb
93
92
  # resource, so the write did not happen.
94
93
  # @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
95
94
  # and body.
96
- def self.create(nuid:, display_name:, bio: nil, orcid: nil, title: nil, on_behalf_of: nil)
97
- body = { nuid: nuid, display_name: display_name, bio: bio, orcid: orcid, title: title }.compact
95
+ def self.create(nuid:, display_name:, bio: nil, orcid: nil, on_behalf_of: nil)
96
+ body = { nuid: nuid, display_name: display_name, bio: bio, orcid: orcid }.compact
98
97
  AtlasRb::Mash.new(write_resource(
99
98
  connection({}, nil, on_behalf_of: on_behalf_of).post(ROUTE, JSON.dump(body))
100
99
  ))["person"]
@@ -107,7 +106,6 @@ module AtlasRb
107
106
  # @param display_name [String, nil]
108
107
  # @param bio [String, nil]
109
108
  # @param orcid [String, nil]
110
- # @param title [String, nil]
111
109
  # @param nuid [String, nil] acting principal.
112
110
  # @param on_behalf_of [String, nil] acting-as target.
113
111
  # @return [AtlasRb::Mash] the unwrapped, updated `"person"` object.
@@ -115,8 +113,8 @@ module AtlasRb
115
113
  # resource, so the write did not happen.
116
114
  # @raise [AtlasRb::ResourceError] on any other non-2xx, carrying Atlas's status
117
115
  # and body.
118
- def self.update(id, display_name: nil, bio: nil, orcid: nil, title: nil, nuid: nil, on_behalf_of: nil)
119
- body = { display_name: display_name, bio: bio, orcid: orcid, title: title }.compact
116
+ def self.update(id, display_name: nil, bio: nil, orcid: nil, nuid: nil, on_behalf_of: nil)
117
+ body = { display_name: display_name, bio: bio, orcid: orcid }.compact
120
118
  AtlasRb::Mash.new(write_resource(
121
119
  connection({}, nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id, JSON.dump(body))
122
120
  ))["person"]
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.9.4
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-03 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