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, #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.

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>)

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



186
187
188
189
190
# File 'lib/atlas_rb/collection.rb', line 186

def self.children(id, nuid: nil, on_behalf_of: nil)
  JSON.parse(
    connection({}, nuid, on_behalf_of: on_behalf_of).get(ROUTE + id + '/children')&.body
  )
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).



75
76
77
78
79
80
81
82
83
84
85
# File 'lib/atlas_rb/collection.rb', line 75

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(JSON.parse(
    connection(params, nuid, on_behalf_of: on_behalf_of).post(ROUTE)&.body
  ))["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.



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

def self.(id, values, nuid: nil, on_behalf_of: nil)
  AtlasRb::Mash.new(JSON.parse(
    connection({ metadata: values }, nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id)&.body
  ))
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)

    the raw response body in the requested format.



289
290
291
292
293
294
# File 'lib/atlas_rb/collection.rb', line 289

def self.mods(id, kind = nil, nuid: nil, on_behalf_of: nil)
  # json default, html, xml
  connection({}, nuid, on_behalf_of: on_behalf_of).get(
    ROUTE + id + '/mods' + (kind.to_s.empty? ? '' : ".#{kind}")
    )&.body
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).



140
141
142
143
144
145
# File 'lib/atlas_rb/collection.rb', line 140

def self.reparent(id, new_parent_id, nuid: nil, on_behalf_of: nil)
  AtlasRb::Mash.new(JSON.parse(
    connection({ parent_id: new_parent_id }, nuid, on_behalf_of: on_behalf_of)
      .patch(ROUTE + id + '/parent')&.body
  ))["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.



102
103
104
105
106
# File 'lib/atlas_rb/collection.rb', line 102

def self.set_featured(id, featured, nuid: nil, on_behalf_of: nil)
  AtlasRb::Mash.new(JSON.parse(
    connection({ featured: featured }, nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id)&.body
  ))["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").



266
267
268
269
270
271
272
# File 'lib/atlas_rb/collection.rb', line 266

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(JSON.parse(
    connection({}, nuid, on_behalf_of: on_behalf_of)
      .patch(ROUTE + id + '/thumbnails', JSON.dump(body))&.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.



165
166
167
# File 'lib/atlas_rb/collection.rb', line 165

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.



206
207
208
209
210
211
212
213
# File 'lib/atlas_rb/collection.rb', line 206

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(JSON.parse(
    multipart(nuid, on_behalf_of: on_behalf_of).patch(ROUTE + id, payload)&.body
  ))
end