atlas_rb 1.1.0 → 1.1.2

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: b3406190d2c893eccbd1b8d47f20efe85ecb01d404145294352e1db61133406b
4
- data.tar.gz: 0a178b48eee743a8e3275dcdf999451b3f811e3af214e163ba675eb4487e5d22
3
+ metadata.gz: 195a3c85a8653e6835bb472b7cda45847e254057075694e37ecfa779efe2dd39
4
+ data.tar.gz: ea58c157df06abcfa8d3f4c6b18fd00e5f87892a8b4231b526ac3e60a8e13510
5
5
  SHA512:
6
- metadata.gz: bde03915384d45748a5c74c7235890e4f64116470f1146775e7480077aac9cf7778d867f526f24dc1470f481837b5b0933794234df3da0254f7eb8aff3ae1539
7
- data.tar.gz: 86bb05b0b0d9b3be8ea8a3f219e1c6137548255312e80a7bf7eabbe809a110993a48dca16a4fe2aaf55def9cd8627389c0796a6e6ed218d054bc945b4068439b
6
+ metadata.gz: 2782eeb3e29454728e68e85144ba5046dd86d9b2ff58eea1cfd704bf2ce03e70f81e977a5b9ad5ffe4091d69d99225746ed2076e875891f358308038983cbfe6
7
+ data.tar.gz: 8dc180f51b17ffdb1feec818445573ec9c25eab3ee4e4245e9afb31f564a14dfbc93db40e3bd174a078e89f31bd57c240eb85b17f69828494ebaab3c0f23f9f4
data/.version CHANGED
@@ -1 +1 @@
1
- 1.1.0
1
+ 1.1.2
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.1
4
+
5
+ ### Added
6
+
7
+ - **`depositor:` kwarg on `AtlasRb::Work.create`** — optional NUID
8
+ forwarded as the `depositor` query param on `POST /works`. When
9
+ omitted, behaviour is unchanged: Atlas defaults the depositor to the
10
+ acting user. When provided, Atlas stamps the named NUID as the Work's
11
+ `depositor` and records the acting user as the `proxy_uploader`.
12
+
13
+ Motivation: proxy deposit. Librarians and bulk-deposit jobs frequently
14
+ upload Works on behalf of a researcher who is the rightful credited
15
+ depositor. Until now there was no way to express that split through
16
+ the gem — callers had to choose between misattributing the deposit to
17
+ the librarian or dropping to a raw Faraday call. The depositor is
18
+ immutable post-create; there is no corresponding setter on the update
19
+ surface.
20
+
3
21
  ## 1.1.0
4
22
 
5
23
  ### Added
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- atlas_rb (1.1.0)
4
+ atlas_rb (1.1.2)
5
5
  faraday (~> 2.7)
6
6
  faraday-follow_redirects (~> 0.3.0)
7
7
  faraday-multipart (~> 1)
@@ -23,7 +23,7 @@ GEM
23
23
  net-http (~> 0.5)
24
24
  hashie (5.1.0)
25
25
  logger
26
- json (2.19.5)
26
+ json (2.19.7)
27
27
  logger (1.7.0)
28
28
  multipart-post (2.4.1)
29
29
  net-http (0.9.1)
@@ -173,6 +173,9 @@ module AtlasRb
173
173
  # `User:` header. Required for cerberus-token requests; legacy bearer
174
174
  # tokens still resolve without it.
175
175
  # @return [AtlasRb::Mash] the parsed JSON response.
176
+ # @raise [AtlasRb::StaleResourceError] if Atlas reports an optimistic-lock
177
+ # conflict that exhausted its internal retry budget (HTTP 409 with
178
+ # `error: "stale_resource"`).
176
179
  #
177
180
  # @example
178
181
  # AtlasRb::Collection.set_thumbnails(
@@ -179,6 +179,9 @@ module AtlasRb
179
179
  # `User:` header. Required for cerberus-token requests; legacy bearer
180
180
  # tokens still resolve without it.
181
181
  # @return [AtlasRb::Mash] the parsed JSON response.
182
+ # @raise [AtlasRb::StaleResourceError] if Atlas reports an optimistic-lock
183
+ # conflict that exhausted its internal retry budget (HTTP 409 with
184
+ # `error: "stale_resource"`).
182
185
  #
183
186
  # @example
184
187
  # AtlasRb::Community.set_thumbnails(
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AtlasRb
4
+ # Base error for atlas_rb. Subclassed for specific wire-level conditions
5
+ # that callers want to handle distinctly. Most non-2xx responses still
6
+ # flow through as Mashes today; we mint typed exceptions only where
7
+ # callers genuinely need to discriminate (currently: optimistic-lock
8
+ # conflicts that ActiveJob `retry_on` policies need to key on).
9
+ class Error < StandardError; end
10
+
11
+ # Raised when Atlas responds with HTTP 409 + `error: "stale_resource"`,
12
+ # indicating an optimistic-lock conflict that either (a) exhausted
13
+ # Atlas's internal retry budget for a retry-safe action, or (b) hit a
14
+ # retry-unsafe action and surfaced immediately.
15
+ #
16
+ # Callers (typically ActiveJob subclasses in Cerberus) handle this via:
17
+ #
18
+ # retry_on AtlasRb::StaleResourceError, attempts: 5, wait: :polynomially_longer
19
+ #
20
+ # The exception carries the resource_id and action from Atlas's envelope
21
+ # so failure logs are useful without needing the full HTTP response.
22
+ class StaleResourceError < Error
23
+ # @return [String, nil] the conflicted resource's ID, from the envelope.
24
+ attr_reader :resource_id
25
+
26
+ # @return [String, nil] the controller action that conflicted, from the
27
+ # envelope (e.g. `"update_thumbnails"`).
28
+ attr_reader :action
29
+
30
+ # @param message [String] human-readable conflict description.
31
+ # @param resource_id [String, nil] the conflicted resource's ID.
32
+ # @param action [String, nil] the controller action that conflicted.
33
+ def initialize(message, resource_id: nil, action: nil)
34
+ super(message)
35
+ @resource_id = resource_id
36
+ @action = action
37
+ end
38
+ end
39
+ end
@@ -59,6 +59,7 @@ module AtlasRb
59
59
  params: params,
60
60
  headers: headers
61
61
  ) do |f|
62
+ f.use AtlasRb::Middleware::RaiseOnStaleResource
62
63
  f.response :follow_redirects
63
64
  f.adapter Faraday.default_adapter
64
65
  end
@@ -103,6 +104,7 @@ module AtlasRb
103
104
  url: ENV.fetch("ATLAS_URL", nil),
104
105
  headers: headers
105
106
  ) do |f|
107
+ f.use AtlasRb::Middleware::RaiseOnStaleResource
106
108
  f.request :multipart
107
109
  f.request :url_encoded
108
110
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AtlasRb
4
+ # Faraday middleware namespace.
5
+ module Middleware
6
+ # Translates Atlas's structured optimistic-lock conflict response into a
7
+ # typed Ruby exception.
8
+ #
9
+ # Atlas surfaces an exhausted-retry (or retry-unsafe) optimistic-lock
10
+ # conflict as an HTTP `409 Conflict` whose JSON body carries the
11
+ # discriminator `error: "stale_resource"`. This middleware keys on the
12
+ # **status + discriminator pair** and raises {AtlasRb::StaleResourceError},
13
+ # carrying the envelope's `resource_id` and `action` through so callers'
14
+ # failure logs are useful without the full response.
15
+ #
16
+ # It is intentionally narrow: any other status, or a 409 without the
17
+ # discriminator, passes through untouched so the caller still sees the
18
+ # response as a Mash (see {AtlasRb::StaleResourceError} for the rationale —
19
+ # atlas_rb stays a thin Faraday binding and translates only the one wire
20
+ # signal Cerberus jobs need to discriminate on).
21
+ class RaiseOnStaleResource < Faraday::Middleware
22
+ # @param env [Faraday::Env] the completed response environment.
23
+ # @raise [AtlasRb::StaleResourceError] on a 409 whose body carries
24
+ # `error: "stale_resource"`.
25
+ # @return [void]
26
+ def on_complete(env)
27
+ return unless env.status == 409
28
+
29
+ body = parse_json(env.body)
30
+ return unless body.is_a?(Hash) && body["error"] == "stale_resource"
31
+
32
+ raise AtlasRb::StaleResourceError.new(
33
+ body["message"] || "Atlas reported a stale-resource conflict",
34
+ resource_id: body["resource_id"],
35
+ action: body["action"]
36
+ )
37
+ end
38
+
39
+ private
40
+
41
+ def parse_json(body)
42
+ return body if body.is_a?(Hash)
43
+
44
+ JSON.parse(body.to_s)
45
+ rescue JSON::ParserError
46
+ nil
47
+ end
48
+ end
49
+ end
50
+ end
data/lib/atlas_rb/work.rb CHANGED
@@ -93,6 +93,13 @@ module AtlasRb
93
93
  # @param on_behalf_of [String, nil] optional NUID for the `On-Behalf-Of`
94
94
  # header. Falls through to {AtlasRb.config}.default_on_behalf_of when
95
95
  # omitted.
96
+ # @param depositor [String, nil] optional NUID to stamp on the new Work's
97
+ # `depositor` field. When omitted, Atlas defaults the depositor to the
98
+ # acting user (`nuid:`); this kwarg is the proxy / batch escape hatch
99
+ # where the librarian who uploaded the Work is distinct from the person
100
+ # it should be attributed to. The acting user becomes the Work's
101
+ # `proxy_uploader`. The depositor is immutable post-create; there is no
102
+ # setter on the update surface.
96
103
  # @return [Hash] the created Work payload (post-update if `xml_path` was
97
104
  # supplied).
98
105
  #
@@ -105,9 +112,15 @@ module AtlasRb
105
112
  # @example Retry-safe bulk-deposit create
106
113
  # key = SecureRandom.uuid
107
114
  # AtlasRb::Work.create("col-456", idempotency_key: key)
108
- def self.create(id, xml_path = nil, idempotency_key: nil, nuid: nil, on_behalf_of: nil)
115
+ #
116
+ # @example Proxy deposit — librarian uploads on behalf of a researcher
117
+ # AtlasRb::Work.create("col-456", depositor: "000000123")
118
+ def self.create(id, xml_path = nil, idempotency_key: nil, nuid: nil,
119
+ on_behalf_of: nil, depositor: nil)
120
+ params = { collection_id: id }
121
+ params[:depositor] = depositor if depositor
109
122
  result = AtlasRb::Mash.new(JSON.parse(
110
- connection({ collection_id: id }, nuid,
123
+ connection(params, nuid,
111
124
  on_behalf_of: on_behalf_of, idempotency_key: idempotency_key).post(ROUTE)&.body
112
125
  ))["work"]
113
126
  return result unless xml_path.present?
@@ -157,6 +170,9 @@ module AtlasRb
157
170
  # header. Falls through to {AtlasRb.config}.default_on_behalf_of when
158
171
  # omitted.
159
172
  # @return [Faraday::Response] the raw response. Status `200` on success.
173
+ # @raise [AtlasRb::StaleResourceError] if Atlas reports an optimistic-lock
174
+ # conflict that exhausted its internal retry budget (HTTP 409 with
175
+ # `error: "stale_resource"`).
160
176
  #
161
177
  # @example
162
178
  # AtlasRb::Work.complete("w-789")
@@ -230,6 +246,9 @@ module AtlasRb
230
246
  # `User:` header. Required for cerberus-token requests; legacy bearer
231
247
  # tokens still resolve without it.
232
248
  # @return [AtlasRb::Mash] the parsed JSON response.
249
+ # @raise [AtlasRb::StaleResourceError] if Atlas reports an optimistic-lock
250
+ # conflict that exhausted its internal retry budget (HTTP 409 with
251
+ # `error: "stale_resource"`).
233
252
  #
234
253
  # @example
235
254
  # AtlasRb::Work.set_thumbnails(
@@ -263,6 +282,9 @@ module AtlasRb
263
282
  # `User:` header. Required for cerberus-token requests; legacy bearer
264
283
  # tokens still resolve without it.
265
284
  # @return [AtlasRb::Mash] the parsed JSON response.
285
+ # @raise [AtlasRb::StaleResourceError] if Atlas reports an optimistic-lock
286
+ # conflict that exhausted its internal retry budget (HTTP 409 with
287
+ # `error: "stale_resource"`).
266
288
  #
267
289
  # @example
268
290
  # AtlasRb::Work.set_image_derivatives(
data/lib/atlas_rb.rb CHANGED
@@ -4,7 +4,9 @@ require "faraday"
4
4
  require "faraday/multipart"
5
5
  require "faraday/follow_redirects"
6
6
  require_relative "atlas_rb/version"
7
+ require_relative "atlas_rb/errors"
7
8
  require_relative "atlas_rb/configuration"
9
+ require_relative "atlas_rb/middleware/raise_on_stale_resource"
8
10
  require_relative "atlas_rb/faraday_helper"
9
11
  require_relative "atlas_rb/mash"
10
12
  require_relative "atlas_rb/authentication"
@@ -69,10 +71,8 @@ require_relative "atlas_rb/system/user"
69
71
  # AtlasRb::Blob.content(blob["id"]) { |chunk| f.write(chunk) }
70
72
  # end
71
73
  module AtlasRb
72
- # Generic error raised by future code paths; not currently used by any
73
- # resource class. Atlas errors today surface as raw `Faraday::Response`
74
- # objects or `JSON::ParserError`s on malformed bodies.
75
- class Error < StandardError; end
74
+ # The error hierarchy ({AtlasRb::Error}, {AtlasRb::StaleResourceError}) lives
75
+ # in `atlas_rb/errors.rb`, required above.
76
76
 
77
77
  # The gem-wide configuration instance. Lazily initialized — host
78
78
  # applications register defaults via {AtlasRb.configure}.
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.1.0
4
+ version: 1.1.2
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-05-26 00:00:00.000000000 Z
11
+ date: 2026-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -125,9 +125,11 @@ files:
125
125
  - lib/atlas_rb/community.rb
126
126
  - lib/atlas_rb/configuration.rb
127
127
  - lib/atlas_rb/delegate.rb
128
+ - lib/atlas_rb/errors.rb
128
129
  - lib/atlas_rb/faraday_helper.rb
129
130
  - lib/atlas_rb/file_set.rb
130
131
  - lib/atlas_rb/mash.rb
132
+ - lib/atlas_rb/middleware/raise_on_stale_resource.rb
131
133
  - lib/atlas_rb/resource.rb
132
134
  - lib/atlas_rb/system/user.rb
133
135
  - lib/atlas_rb/version.rb