atlas_rb 1.8.9 → 1.8.11

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: 1fd50831467b065989eb0ec39af5f96b87622c5d0e161e0e6a5bdef61764505a
4
- data.tar.gz: 55c36e14086a0fab3776771c797af98c9a431cca3f0b09f14ffac478ab58067c
3
+ metadata.gz: f6edbe1b05256eaab4d296a11f358bb7c7c3f9eaf439be4d48c41e75cfea4951
4
+ data.tar.gz: 8e042786b3c7636f7ad3676b9086a7db4ecfa8efed7ed1e91ac8ec4153dbb320
5
5
  SHA512:
6
- metadata.gz: 7aea8252388df4686a31174ca154b7e76da1d97dd16a8509f773bd2b918dfb282ccb93960f5138b0581df9d32d20c45a0f8cf4a1475dda787468644e1d83cdbd
7
- data.tar.gz: 25e3ecf9a7cc353645a8a1ad76700523bba185895fcf1df13b9b098549a45287fb3a9c41abb0eefa559f5e1d8b4c8c4b81b7c16e5e418f4cee39e5f0d554dc56
6
+ metadata.gz: 44832489abc704196a42b92d85304e886cc3951e711ea33980c9357cb515a4f3e674d7c75db989b7de5c7fbdc9ed36dcfc51a5eceb1d697371d3971227a7e5fa
7
+ data.tar.gz: 45d0ed33989b889a4a2f3ae3c9932b73f85daa3bc7f5ce9e859f7ea304bf97c71195694322e4e78cdcb537e8f50f37cd1b1d1fe6f7c76bc8458f780688e5c143
data/.version CHANGED
@@ -1 +1 @@
1
- 1.8.9
1
+ 1.8.11
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- atlas_rb (1.8.9)
4
+ atlas_rb (1.8.11)
5
5
  faraday (~> 2.7)
6
6
  faraday-follow_redirects (~> 0.3.0)
7
7
  faraday-multipart (~> 1)
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AtlasRb
4
- # A grouping of {Work}s within a {Community}.
4
+ # A grouping of {Work}s and nested {Collection}s, parented by a {Community}
5
+ # or another Collection.
5
6
  #
6
- # Collections are the leaf containers in the organizational tree they hold
7
- # Works directly. Every Collection has
8
- # exactly one parent Community.
7
+ # Collections nest, exactly as they did in DRS v1: a Collection holds Works
8
+ # and/or child Collections, and its own parent may be a Community or a
9
+ # Collection (Community Community|Collection, Collection → Collection|Work,
10
+ # Work → leaf). A consumer that flattens a subtree must recurse into
11
+ # descendant Collections rather than stopping at direct children.
9
12
  #
10
13
  # See also: {Community}, {Work}.
11
14
  class Collection < Resource
@@ -35,13 +38,13 @@ module AtlasRb
35
38
  body && AtlasRb::Mash.new(body)["collection"]
36
39
  end
37
40
 
38
- # Create a new Collection under an existing Community.
41
+ # Create a new Collection under an existing Community or parent Collection.
39
42
  #
40
- # **Note**: unlike {Community.create}, the `id` parameter here is the
41
- # parent **Community** ID (not a parent Collection ID Collections do
42
- # not nest).
43
+ # The `id` parameter is the parent's ID — a **Community or a Collection**,
44
+ # since Collections nest (a Collection may hold child Collections as well as
45
+ # Works).
43
46
  #
44
- # @param id [String] the parent Community ID.
47
+ # @param id [String] the parent Community or Collection ID.
45
48
  # @param xml_path [String, nil] optional path to a MODS XML file used to
46
49
  # seed metadata. When given, the Collection is created and immediately
47
50
  # patched with the metadata in the file.
@@ -63,7 +66,7 @@ module AtlasRb
63
66
  result = AtlasRb::Mash.new(JSON.parse(
64
67
  connection({ parent_id: id, featured: featured }, nuid, on_behalf_of: on_behalf_of).post(ROUTE)&.body
65
68
  ))["collection"]
66
- return result unless xml_path.present?
69
+ return result if xml_path.to_s.empty?
67
70
 
68
71
  update(result["id"], xml_path, nuid: nuid, on_behalf_of: on_behalf_of)
69
72
  find(result["id"], nuid: nuid, on_behalf_of: on_behalf_of)
@@ -90,10 +93,10 @@ module AtlasRb
90
93
  ))["collection"]
91
94
  end
92
95
 
93
- # Move a Collection to a different parent Community.
96
+ # Move a Collection to a different parent (Community or Collection).
94
97
  #
95
98
  # Wraps `PATCH /collections/<id>/parent` with a `parent_id` of the new
96
- # Community. Atlas re-parents the Collection and synchronously cascades
99
+ # parent. Atlas re-parents the Collection and synchronously cascades
97
100
  # the ancestry index over its Works; the structural rules (type, cycle,
98
101
  # tombstone guards) are enforced server-side and surface as a `422`.
99
102
  #
@@ -101,7 +104,7 @@ module AtlasRb
101
104
  # the only difference is the verb and that the Collection already exists.
102
105
  #
103
106
  # @param id [String] the Collection ID to move.
104
- # @param new_parent_id [String] the destination Community ID.
107
+ # @param new_parent_id [String] the destination Community or Collection ID.
105
108
  # @param nuid [String, nil] optional acting user's NUID. On the relay-signing
106
109
  # path it is signed into the assertion `sub`; on the BYO-JWT (`ATLAS_JWT`)
107
110
  # path it is ignored (identity lives in the token).
@@ -274,7 +277,7 @@ module AtlasRb
274
277
  def self.mods(id, kind = nil, nuid: nil, on_behalf_of: nil)
275
278
  # json default, html, xml
276
279
  connection({}, nuid, on_behalf_of: on_behalf_of).get(
277
- ROUTE + id + '/mods' + (kind.present? ? ".#{kind}" : '')
280
+ ROUTE + id + '/mods' + (kind.to_s.empty? ? '' : ".#{kind}")
278
281
  )&.body
279
282
  end
280
283
  end
@@ -64,7 +64,7 @@ module AtlasRb
64
64
  result = AtlasRb::Mash.new(JSON.parse(
65
65
  connection({ parent_id: id }, nuid, on_behalf_of: on_behalf_of).post(ROUTE)&.body
66
66
  ))["community"]
67
- return result unless xml_path.present?
67
+ return result if xml_path.to_s.empty?
68
68
 
69
69
  update(result["id"], xml_path, nuid: nuid, on_behalf_of: on_behalf_of)
70
70
  find(result["id"], nuid: nuid, on_behalf_of: on_behalf_of)
@@ -263,7 +263,7 @@ module AtlasRb
263
263
  def self.mods(id, kind = nil, nuid: nil, on_behalf_of: nil)
264
264
  # json default, html, xml
265
265
  connection({}, nuid, on_behalf_of: on_behalf_of).get(
266
- ROUTE + id + '/mods' + (kind.present? ? ".#{kind}" : '')
266
+ ROUTE + id + '/mods' + (kind.to_s.empty? ? '' : ".#{kind}")
267
267
  )&.body
268
268
  end
269
269
  end
@@ -36,9 +36,28 @@ module AtlasRb
36
36
  # If neither a signing key nor `ATLAS_JWT` is configured there is no relay
37
37
  # credential, so the transport raises {AtlasRb::ConfigurationError}.
38
38
  #
39
+ # ## Instrumentation
40
+ #
41
+ # Every builder brackets its request in an `ActiveSupport::Notifications`
42
+ # event named **`request.atlas_rb`** (Faraday's `:instrumentation`
43
+ # middleware, wired as the *outermost* handler so a redirect hop — e.g. the
44
+ # `/resources/:noid` NOID resolver — folds into one event per logical call
45
+ # rather than being double-counted). The payload is the Faraday `env`, so a
46
+ # subscriber reads `payload.method` / `payload.url` and the event's duration.
47
+ # This lets a host count/time Atlas round-trips (an N+1-over-HTTP detector)
48
+ # without reaching into gem internals. It is guarded on
49
+ # `defined?(ActiveSupport::Notifications)`: Rails hosts get the events; the
50
+ # headless BYO-JWT path (no AS loaded) is unaffected. With no subscriber
51
+ # attached the emit degrades to a listener lookup + `yield`, so it is safe to
52
+ # leave in the stack permanently — opt-in lives entirely on the consumer side.
53
+ #
39
54
  # The module is mixed in via `extend`, so its methods become class methods on
40
55
  # the host (e.g. `AtlasRb::Work.connection({})`).
41
56
  module FaradayHelper
57
+ # ActiveSupport::Notifications event name emitted per outbound Atlas request.
58
+ # A dedicated name (not Faraday's default `request.faraday`) so a host that
59
+ # also uses Faraday for other clients can subscribe to Atlas traffic alone.
60
+ INSTRUMENTATION_EVENT = "request.atlas_rb"
42
61
  # Wire contract Atlas enforces for relay-signing assertions (see Atlas
43
62
  # ApplicationController#verify_cerberus_assertion). iss/aud are fixed; the
44
63
  # short TTL bounds replay (Atlas allows 30s leeway on exp).
@@ -83,6 +102,7 @@ module AtlasRb
83
102
  params: params,
84
103
  headers: headers
85
104
  ) do |f|
105
+ instrument(f)
86
106
  f.use AtlasRb::Middleware::RaiseOnStaleResource
87
107
  f.use AtlasRb::Middleware::RaiseOnResourceError
88
108
  f.response :follow_redirects
@@ -123,6 +143,7 @@ module AtlasRb
123
143
  url: ENV.fetch("ATLAS_URL", nil),
124
144
  headers: headers
125
145
  ) do |f|
146
+ instrument(f)
126
147
  f.use AtlasRb::Middleware::RaiseOnStaleResource
127
148
  # Translate Atlas's verify-on-ingest 422 (fixity_mismatch /
128
149
  # unsupported_digest_algorithm) into a typed FixityMismatchError —
@@ -190,6 +211,7 @@ module AtlasRb
190
211
  params: params,
191
212
  headers: headers
192
213
  ) do |f|
214
+ instrument(f)
193
215
  f.response :follow_redirects
194
216
  f.adapter Faraday.default_adapter
195
217
  end
@@ -197,6 +219,20 @@ module AtlasRb
197
219
 
198
220
  private
199
221
 
222
+ # Register Faraday's instrumentation middleware as the OUTERMOST handler so
223
+ # each logical call emits exactly one {INSTRUMENTATION_EVENT} — a redirect
224
+ # hop (e.g. the `/resources/:noid` resolver) is bracketed with the request
225
+ # it redirects to, not counted twice. Guarded on
226
+ # `defined?(ActiveSupport::Notifications)` because Faraday defaults its
227
+ # instrumenter to that constant and would `NameError` at build time on the
228
+ # headless path where ActiveSupport isn't loaded; there, the emit is simply
229
+ # never wired and the transport is unaffected.
230
+ def instrument(builder)
231
+ return unless defined?(ActiveSupport::Notifications)
232
+
233
+ builder.request :instrumentation, name: INSTRUMENTATION_EVENT
234
+ end
235
+
200
236
  # Build the auth + identity headers shared by {#connection} and {#multipart}.
201
237
  # Precedence: ATLAS_JWT (BYO-JWT) > relay-signing. The acting nuid /
202
238
  # on_behalf_of fall through to the configured `default_nuid` /
@@ -244,7 +244,7 @@ module AtlasRb
244
244
  # end
245
245
  def self.mods(id, kind = nil, nuid: nil, on_behalf_of: nil)
246
246
  connection({}, nuid, on_behalf_of: on_behalf_of).get(
247
- '/resources/' + id + '/mods' + (kind.present? ? ".#{kind}" : '')
247
+ '/resources/' + id + '/mods' + (kind.to_s.empty? ? '' : ".#{kind}")
248
248
  )&.body
249
249
  end
250
250
 
@@ -320,7 +320,7 @@ module AtlasRb
320
320
  def self.mods_version(id, version_id, kind: nil, nuid: nil, on_behalf_of: nil)
321
321
  connection({}, nuid, on_behalf_of: on_behalf_of).get(
322
322
  '/resources/' + id + '/mods/versions/' + version_id +
323
- (kind.present? ? ".#{kind}" : '')
323
+ (kind.to_s.empty? ? '' : ".#{kind}")
324
324
  )&.body
325
325
  end
326
326
 
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AtlasRb
4
- # Current gem version, read from the `.version` file at the repo root at load time.
5
- VERSION = File.read(".version").strip
4
+ # Current gem version, read from the `.version` file at the gem root at load
5
+ # time. Resolved relative to this file (not the process CWD) so a standalone
6
+ # consumer — a headless BYO-JWT script run from anywhere — loads the gem's own
7
+ # version rather than raising ENOENT (or reading some unrelated `.version`).
8
+ VERSION = File.read(File.expand_path("../../.version", __dir__)).strip
6
9
  end
data/lib/atlas_rb/work.rb CHANGED
@@ -124,7 +124,7 @@ module AtlasRb
124
124
  connection(params, nuid,
125
125
  on_behalf_of: on_behalf_of, idempotency_key: idempotency_key).post(ROUTE)&.body
126
126
  ))["work"]
127
- return result unless xml_path.present?
127
+ return result if xml_path.to_s.empty?
128
128
 
129
129
  update(result["id"], xml_path, nuid: nuid, on_behalf_of: on_behalf_of)
130
130
  find(result["id"], nuid: nuid, on_behalf_of: on_behalf_of)
@@ -559,7 +559,7 @@ module AtlasRb
559
559
  def self.mods(id, kind = nil, nuid: nil, on_behalf_of: nil)
560
560
  # json default, html, xml
561
561
  connection({}, nuid, on_behalf_of: on_behalf_of).get(
562
- ROUTE + id + '/mods' + (kind.present? ? ".#{kind}" : '')
562
+ ROUTE + id + '/mods' + (kind.to_s.empty? ? '' : ".#{kind}")
563
563
  )&.body
564
564
  end
565
565
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: atlas_rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.9
4
+ version: 1.8.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cliff