Class: AtlasRb::Collection

Inherits:
Resource show all
Defined in:
lib/atlas_rb/collection.rb

Overview

A grouping of Works and nested Collections, parented by a Community or another Collection.

Collections nest, exactly as they did in DRS v1: a Collection holds Works and/or child Collections, and its own parent may be a Community or a Collection (Community → Community|Collection, Collection → Collection|Work, Work → leaf). A consumer that flattens a subtree must recurse into descendant Collections rather than stopping at direct children.

See also: Community, Work.

Constant Summary collapse

ROUTE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Atlas REST endpoint prefix for this resource.

"/collections/"

Constants included from FaradayHelper

FaradayHelper::ASSERTION_AUDIENCE, FaradayHelper::ASSERTION_ISSUER, FaradayHelper::ASSERTION_TTL, FaradayHelper::INSTRUMENTATION_EVENT

Class Method Summary collapse

Methods inherited from Resource

descendant_works, find_many, history, mods_version, mods_versions, permissions, preview

Methods included from FaradayHelper

#connection, #multipart, #read_body, #read_raw, #system_connection, #with_file_part

Class Method Details

.children(id, nuid: nil, on_behalf_of: nil) ⇒ Array<String>?

List the Works in a Collection.

The endpoint returns just the noids; resolve each through Resource.find (or Work.find) when a full payload is needed.

children answers noids only. To render them, resolve the whole list in one call with Resource.find_many, which returns a title/thumbnail digest per id — calling find per noid costs a round-trip per child. For a whole subtree flattened to Works, use Resource.descendant_works.

Examples:

AtlasRb::Collection.children("col-456")
# => ["w-789", "w-790"]

Parameters:

  • id (String)

    the Collection ID.

  • nuid (String, nil) (defaults to: nil)

    optional acting user's NUID. On the relay-signing path it is signed into the assertion sub; on the BYO-JWT (ATLAS_JWT) path it is ignored (identity lives in the token).

  • on_behalf_of (String, nil) (defaults to: nil)

    optional NUID for the On-Behalf-Of header. Falls through to AtlasRb.config.default_on_behalf_of when omitted.

Returns:

  • (Array<String>, nil)

    child noids from GET /collections/<id>/children.

    nil when Atlas answers 404 — nothing is there to read, or, with a misconfigured ATLAS_URL, the route is not Atlas's at all.

Raises:

  • (AtlasRb::ResourceError)

    on any non-2xx other than 404 / 410 (an auth or validation envelope, a 5xx, a proxy's 503), carrying Atlas's status and body so the failure is attributable at the boundary.



209
210
211
# File 'lib/atlas_rb/collection.rb', line 209

def self.children(id, nuid: nil, on_behalf_of: nil)
  read_body(connection({}, nuid, on_behalf_of: on_behalf_of).get(ROUTE + id + '/children'))
end

.create(id, xml_path = nil, featured: false, nuid: nil, on_behalf_of: nil, depositor: nil) ⇒ Hash

Create a new Collection under an existing Community or parent Collection.

The id parameter is the parent's ID — a Community or a Collection, since Collections nest (a Collection may hold child Collections as well as Works).

Examples:

A featured showcase collection

AtlasRb::Collection.create("c-123", featured: true)

An institutional container owned by nobody

AtlasRb::Collection.create("c-123", depositor: "000000099")

Parameters:

  • id (String)

    the parent Community or Collection ID.

  • xml_path (String, nil) (defaults to: nil)

    optional path to a MODS XML file used to seed metadata. When given, the Collection is created and immediately patched with the metadata in the file.

  • featured (Boolean) (defaults to: false)

    mark the Collection as a genre-showcase ("Featured") Collection. Defaults to false. Use when provisioning a Community's showcase set (loop create + featured: true).

  • nuid (String, nil) (defaults to: nil)

    optional acting user's NUID. On the relay-signing path it is signed into the assertion sub; on the BYO-JWT (ATLAS_JWT) path it is ignored (identity lives in the token).

  • on_behalf_of (String, nil) (defaults to: nil)

    optional NUID for the On-Behalf-Of header. Falls through to AtlasRb.config.default_on_behalf_of when omitted.

  • depositor (String, nil) (defaults to: nil)

    NUID to stamp as the Collection's intellectual owner. Omit it and Atlas falls through to the acting user. Supply it to attribute a container to someone other than whoever is authorizing the call — e.g. seeding an institutional tree as an admin while attributing it to the anonymous NUID, since nobody personally owns those containers and access to them is via Grouper groups. The depositor is immutable post-create; there is no setter on the update surface.

Returns:

  • (Hash)

    the created Collection payload (post-update if xml_path was supplied).

Raises:



79
80
81
82
83
84
85
86
87
88
89
# File 'lib/atlas_rb/collection.rb', line 79

def self.create(id, xml_path = nil, featured: false, nuid: nil, on_behalf_of: nil, depositor: nil)
  params = { parent_id: id, featured: featured }
  params[:depositor] = depositor if depositor
  result = AtlasRb::Mash.new(write_resource(
    connection(params, nuid, on_behalf_of: on_behalf_of).post(ROUTE)
  ))["collection"]
  return result if xml_path.to_s.empty?

  update(result["id"], xml_path, nuid: nuid, on_behalf_of: on_behalf_of)
  find(result["id"], nuid: nuid, on_behalf_of: on_behalf_of)
end

.find(id, nuid: nil, on_behalf_of: nil) ⇒ Hash?

Fetch a single Collection by ID.

Examples:

AtlasRb::Collection.find("col-456")
# => { "id" => "col-456", "title" => "Faculty Publications", ... }

Parameters:

  • id (String)

    the Collection ID.

  • nuid (String, nil) (defaults to: nil)

    optional acting user's NUID. On the relay-signing path it is signed into the assertion sub; on the BYO-JWT (ATLAS_JWT) path it is ignored (identity lives in the token).

  • on_behalf_of (String, nil) (defaults to: nil)

    optional NUID for the On-Behalf-Of header. Falls through to AtlasRb.config.default_on_behalf_of when omitted.

Returns:

  • (Hash, nil)

    the "collection" object, already unwrapped from the JSON response, or nil when the Collection does not exist (404).

Raises:

  • (AtlasRb::ResourceError)

    on any non-2xx other than 404 / 410 (e.g. an auth/validation error envelope), carrying Atlas's status + body.



36
37
38
39
# File 'lib/atlas_rb/collection.rb', line 36

def self.find(id, nuid: nil, on_behalf_of: nil)
  body = fetch_resource(ROUTE + id, nuid: nuid, on_behalf_of: on_behalf_of)
  body && AtlasRb::Mash.new(body)["collection"]
end

.metadata(id, values, nuid: nil, on_behalf_of: nil) ⇒ Hash

Patch individual descriptive-metadata fields without uploading a full MODS document.

Scoped to user-authored descriptive metadata only. Programmatic writes of machine-set Delegate URIs (thumbnails) have their own purpose-specific endpoint — see set_thumbnails.

Examples:

AtlasRb::Collection.("col-456", title: "Renamed Collection")

Parameters:

  • id (String)

    the Collection ID.

  • values (Hash)

    field-level metadata updates.

  • nuid (String, nil) (defaults to: nil)

    optional acting user's NUID. On the relay-signing path it is signed into the assertion sub; on the BYO-JWT (ATLAS_JWT) path it is ignored (identity lives in the token).

  • on_behalf_of (String, nil) (defaults to: nil)

    optional NUID for the On-Behalf-Of header. Falls through to AtlasRb.config.default_on_behalf_of when omitted.

Returns:

  • (Hash)

    the parsed JSON response.

Raises:



263
264
265
266
267
# File 'lib/atlas_rb/collection.rb', line 263

def self.(id, values, nuid: nil, on_behalf_of: nil)
  AtlasRb::Mash.new(write_resource(
    connection({ metadata: values }, nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id)
  ))
end

.mods(id, kind = nil, nuid: nil, on_behalf_of: nil) ⇒ String?

Fetch the Collection's MODS representation in the requested format.

Examples:

AtlasRb::Collection.mods("col-456", "xml")

Parameters:

  • id (String)

    the Collection ID.

  • kind (String, nil) (defaults to: nil)

    one of "json" (default), "html", or "xml".

  • nuid (String, nil) (defaults to: nil)

    optional acting user's NUID. On the relay-signing path it is signed into the assertion sub; on the BYO-JWT (ATLAS_JWT) path it is ignored (identity lives in the token).

  • on_behalf_of (String, nil) (defaults to: nil)

    optional NUID for the On-Behalf-Of header. Falls through to AtlasRb.config.default_on_behalf_of when omitted.

Returns:

  • (String, nil)

    the raw response body in the requested format.

    nil when Atlas answers 404 — nothing is there to read, or, with a misconfigured ATLAS_URL, the route is not Atlas's at all.

Raises:

  • (AtlasRb::ResourceError)

    on any non-2xx other than 404 / 410 (an auth or validation envelope, a 5xx, a proxy's 503), carrying Atlas's status and body so the failure is attributable at the boundary.



327
328
329
330
331
332
# File 'lib/atlas_rb/collection.rb', line 327

def self.mods(id, kind = nil, nuid: nil, on_behalf_of: nil)
  # json default, html, xml
  read_raw(connection({}, nuid, on_behalf_of: on_behalf_of).get(
             ROUTE + id + '/mods' + (kind.to_s.empty? ? '' : ".#{kind}")
           ))
end

.reparent(id, new_parent_id, nuid: nil, on_behalf_of: nil) ⇒ Hash

Move a Collection to a different parent (Community or Collection).

Wraps PATCH /collections/<id>/parent with a parent_id of the new parent. Atlas re-parents the Collection and synchronously cascades the ancestry index over its Works; the structural rules (type, cycle, tombstone guards) are enforced server-side and surface as a 422.

Mirrors create's "single parent id" shape — same kwarg threading, the only difference is the verb and that the Collection already exists.

Examples:

AtlasRb::Collection.reparent("col-456", "c-999")

Parameters:

  • id (String)

    the Collection ID to move.

  • new_parent_id (String)

    the destination Community or Collection ID.

  • nuid (String, nil) (defaults to: nil)

    optional acting user's NUID. On the relay-signing path it is signed into the assertion sub; on the BYO-JWT (ATLAS_JWT) path it is ignored (identity lives in the token).

  • on_behalf_of (String, nil) (defaults to: nil)

    optional NUID for the On-Behalf-Of header. Falls through to AtlasRb.config.default_on_behalf_of when omitted.

Returns:

  • (Hash)

    the updated "collection" object, already unwrapped — the same shape find returns, reflecting the new a_member_of.

Raises:

  • (AtlasRb::StaleResourceError)

    if Atlas reports an optimistic-lock conflict that exhausted its internal retry budget (HTTP 409 with error: "stale_resource").

  • (AtlasRb::ReparentError)

    if Atlas rejects the move on structural grounds (HTTP 422 — cycle, invalid_parent_type, tombstoned_node, tombstoned_parent, parent_required, parent_not_found). The envelope's error code is exposed as #code.

  • (AtlasRb::ForbiddenError)

    if Atlas refuses the move on authorization grounds (HTTP 403).

  • (AtlasRb::NotFoundError)

    if Atlas answers 404 — the id names no such resource, so the write did not happen.

  • (AtlasRb::ResourceError)

    on any other non-2xx, carrying Atlas's status and body.



152
153
154
155
156
157
# File 'lib/atlas_rb/collection.rb', line 152

def self.reparent(id, new_parent_id, nuid: nil, on_behalf_of: nil)
  AtlasRb::Mash.new(write_resource(
    connection({ parent_id: new_parent_id }, nuid, on_behalf_of: on_behalf_of)
      .patch(ROUTE + id + '/parent')
  ))["collection"]
end

Toggle the showcase "Featured" flag on an existing Collection.

A resource-attribute write (not a MODS update), so it does not touch descriptive metadata. Cerberus reads the projected featured_bsi to badge the Collection in a community's browse.

Examples:

AtlasRb::Collection.set_featured("col-456", true)

Parameters:

  • id (String)

    the Collection ID.

  • featured (Boolean)

    the new flag value.

  • nuid (String, nil) (defaults to: nil)

    optional acting user's NUID.

  • on_behalf_of (String, nil) (defaults to: nil)

    optional NUID for the On-Behalf-Of header. Falls through to AtlasRb.config.default_on_behalf_of when omitted.

Returns:

  • (AtlasRb::Mash)

    the updated "collection" object, already unwrapped.

Raises:



110
111
112
113
114
# File 'lib/atlas_rb/collection.rb', line 110

def self.set_featured(id, featured, nuid: nil, on_behalf_of: nil)
  AtlasRb::Mash.new(write_resource(
    connection({ featured: featured }, nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id)
  ))["collection"]
end

.set_thumbnails(id, thumbnail: nil, thumbnail_2x: nil, preview: nil, nuid: nil, on_behalf_of: nil) ⇒ AtlasRb::Mash

Attach the three thumbnail/preview Delegate URIs to a Collection.

Collection-level mirror of Work.set_thumbnails. Atlas dispatches each non-blank URI to its matching Delegate role (thumbnail_image / thumbnail_image_2x / preview_image) via DelegateUpdater. Missing keys are left untouched.

Examples:

AtlasRb::Collection.set_thumbnails(
  "col-456",
  thumbnail:    "https://iiif.example.edu/iiif/3/c.jp2/full/!85,85/0/default.jpg",
  thumbnail_2x: "https://iiif.example.edu/iiif/3/c.jp2/full/!170,170/0/default.jpg",
  preview:      "https://iiif.example.edu/iiif/3/c.jp2/full/500,/0/default.jpg"
)

Parameters:

  • id (String)

    the Collection ID.

  • thumbnail (String, nil) (defaults to: nil)

    IIIF URI for the ~85² thumbnail.

  • thumbnail_2x (String, nil) (defaults to: nil)

    IIIF URI for the ~170² 2x thumbnail.

  • preview (String, nil) (defaults to: nil)

    IIIF URI for the ~500w preview image.

  • nuid (String, nil) (defaults to: nil)

    optional acting user's NUID. On the relay-signing path it is signed into the assertion sub; on the BYO-JWT (ATLAS_JWT) path it is ignored (identity lives in the token).

Returns:

Raises:

  • (AtlasRb::StaleResourceError)

    if Atlas reports an optimistic-lock conflict that exhausted its internal retry budget (HTTP 409 with error: "stale_resource").

  • (AtlasRb::NotFoundError)

    if Atlas answers 404 — the id names no such resource, so the write did not happen.

  • (AtlasRb::ResourceError)

    on any other non-2xx, carrying Atlas's status and body.



299
300
301
302
303
304
305
# File 'lib/atlas_rb/collection.rb', line 299

def self.set_thumbnails(id, thumbnail: nil, thumbnail_2x: nil, preview: nil, nuid: nil, on_behalf_of: nil)
  body = { thumbnail: thumbnail, thumbnail_2x: thumbnail_2x, preview: preview }.compact
  AtlasRb::Mash.new(write_resource(
    connection({}, nuid, on_behalf_of: on_behalf_of)
      .patch(ROUTE + id + '/thumbnails', JSON.dump(body))
  ))
end

.tombstone(id, nuid: nil, on_behalf_of: nil) ⇒ Faraday::Response

Tombstone (withdraw) a Collection.

The Collection remains in Atlas storage but is marked as withdrawn: search and show pages return a withdrawn stub for every user. Atlas rejects the request with 422 has_live_children if the Collection still has live (non-tombstoned) Works.

Examples:

AtlasRb::Collection.tombstone("col-456", nuid: "000000002")

Parameters:

  • id (String)

    the Collection ID.

  • nuid (String) (defaults to: nil)

    the acting user's NUID, stamped on the resource as tombstoned_by for audit purposes.

  • on_behalf_of (String, nil) (defaults to: nil)

    optional NUID for the On-Behalf-Of header. Falls through to AtlasRb.config.default_on_behalf_of when omitted.

Returns:

  • (Faraday::Response)

    the raw response. 200/204 on success; 422 with {"code":"has_live_children"} if the Collection is not empty.



177
178
179
# File 'lib/atlas_rb/collection.rb', line 177

def self.tombstone(id, nuid: nil, on_behalf_of: nil)
  connection({}, nuid, on_behalf_of: on_behalf_of).post(ROUTE + id + '/tombstone')
end

.update(id, xml_path, nuid: nil, on_behalf_of: nil) ⇒ Hash

Replace a Collection's metadata by uploading a MODS XML document.

Examples:

AtlasRb::Collection.update("col-456", "/tmp/collection-mods.xml")

Parameters:

  • id (String)

    the Collection ID.

  • xml_path (String)

    path to a MODS XML file on disk.

  • nuid (String, nil) (defaults to: nil)

    optional acting user's NUID. On the relay-signing path it is signed into the assertion sub; on the BYO-JWT (ATLAS_JWT) path it is ignored (identity lives in the token).

  • on_behalf_of (String, nil) (defaults to: nil)

    optional NUID for the On-Behalf-Of header. Falls through to AtlasRb.config.default_on_behalf_of when omitted.

Returns:

  • (Hash)

    the parsed JSON response from the patch.

Raises:



231
232
233
234
235
236
237
238
# File 'lib/atlas_rb/collection.rb', line 231

def self.update(id, xml_path, nuid: nil, on_behalf_of: nil)
  payload = { binary: Faraday::Multipart::FilePart.new(File.open(xml_path),
                                                       "application/xml",
                                                       File.basename(xml_path)) }
  AtlasRb::Mash.new(write_resource(
    multipart(nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id, payload)
  ))
end