lib/dor/services/client/object.rb
# frozen_string_literal: true
require 'deprecation'
module Dor
module Services
class Client
# API calls that are about a repository object
class Object < VersionedService # rubocop:disable Metrics/ClassLength
extend Deprecation
attr_reader :object_identifier
# @param object_identifier [String] the pid for the object
def initialize(connection:, version:, object_identifier:)
raise ArgumentError, "The `object_identifier` parameter must be an identifier string: #{object_identifier.inspect}" unless object_identifier.is_a?(String)
super(connection: connection, version: version)
@object_identifier = object_identifier
end
def events
@events ||= Events.new(**parent_params)
end
def workspace
@workspace ||= Workspace.new(**parent_params)
end
def administrative_tags
@administrative_tags ||= AdministrativeTags.new(**parent_params)
end
def release_tags
@release_tags ||= ReleaseTags.new(**parent_params)
end
def version
@version ||= ObjectVersion.new(**parent_params)
end
def accession(params = {})
@accession ||= Accession.new(**parent_params.merge(params))
end
# Retrieves the Cocina model
# @param [boolean] validate validate the response object
# @raise [NotFoundResponse] when the response is a 404 (object not found)
# @raise [UnexpectedResponse] when the response is not successful.
# @return [Cocina::Models::DROWithMetadata,Cocina::Models::CollectionWithMetadata,Cocina::Models::AdminPolicyWithMetadata] the returned model
def find(validate: false)
resp = connection.get do |req|
req.url object_path
end
raise_exception_based_on_response!(resp) unless resp.success?
build_cocina_from_response(resp, validate: validate)
end
BASE_ALLOWED_FIELDS = %i[external_identifier cocina_version label version administrative description].freeze
DRO_ALLOWED_FIELDS = BASE_ALLOWED_FIELDS + %i[content_type access identification structural geographic]
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/ParameterLists
def find_lite(administrative: true, description: true, access: true, structural: true, identification: true, geographic: true)
fields = []
fields << :administrative if administrative
fields << :description if description
fields << :access if access
fields << :structural if structural
fields << :identification if identification
fields << :geographic if geographic
resp = connection.post '/graphql', query(fields),
'Content-Type' => 'application/json'
raise_exception_based_on_response!(resp) unless resp.success?
resp_json = JSON.parse(resp.body)
# GraphQL returns 200 even when an error
raise_graphql_exception(resp, resp_json)
Cocina::Models.build_lite(resp_json['data']['cocinaObject'])
end
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/ParameterLists
# Get a list of the collections. (Similar to Valkyrie's find_inverse_references_by)
# @raise [UnexpectedResponse] if the request is unsuccessful.
# @return [Array<Cocina::Models::DRO>]
def collections
Collections.new(**parent_params).collections
end
# Get a list of the members
# @raise [UnexpectedResponse] if the request is unsuccessful.
# @return [Array<Members::Member>]
def members
Members.new(**parent_params).members
end
def transfer
Transfer.new(**parent_params)
end
delegate :publish, :unpublish, :preserve, :shelve, to: :transfer
def mutate
Mutate.new(**parent_params)
end
delegate :refresh_descriptive_metadata_from_ils, :update, :destroy, :apply_admin_policy_defaults, to: :mutate
alias refresh_metadata refresh_descriptive_metadata_from_ils
deprecation_deprecate refresh_metadata: 'Use refresh_descriptive_metadata_from_ils instead'
# Update the marc record for the given object
# @raise [NotFoundResponse] when the response is a 404 (object not found)
# @raise [UnexpectedResponse] when the response is not successful.
# @return [boolean] true on success
def update_marc_record
resp = connection.post do |req|
req.url "#{object_path}/update_marc_record"
end
return true if resp.success?
raise_exception_based_on_response!(resp)
end
# Update the DOI metadata at DataCite
# @raise [NotFoundResponse] when the response is a 404 (object not found)
# @return [boolean] true on success
def update_doi_metadata
resp = connection.post do |req|
req.url "#{object_path}/update_doi_metadata"
end
return true if resp.success?
raise_exception_based_on_response!(resp)
end
# Update the ORCID Work
# @raise [NotFoundResponse] when the response is a 404 (object not found)
# @return [Boolean] true on success
def update_orcid_work
resp = connection.post do |req|
req.url "#{object_path}/update_orcid_work"
end
return true if resp.success?
raise_exception_based_on_response!(resp)
end
# Notify the external Goobi system for a new object that was registered in DOR
# @raise [NotFoundResponse] when the response is a 404 (object not found)
# @raise [UnexpectedResponse] when the response is not successful.
# @return [boolean] true on success
def notify_goobi
resp = connection.post do |req|
req.url "#{object_path}/notify_goobi"
end
return true if resp.success?
raise_exception_based_on_response!(resp)
end
private
def parent_params
{ connection: connection, version: api_version, object_identifier: object_identifier }
end
def object_path
"#{api_version}/objects/#{object_identifier}"
end
DEFAULT_FIELDS = %i[externalIdentifier type version label cocinaVersion].freeze
def query(fields)
all_fields = DEFAULT_FIELDS + fields
{
query:
<<~GQL
{
cocinaObject(externalIdentifier: "#{object_identifier}") {
#{all_fields.join("\n")}
}
}
GQL
}.to_json
end
def raise_graphql_exception(resp, resp_json)
return unless resp_json['errors'].present?
exception_class = not_found_exception?(resp_json['errors'].first) ? NotFoundResponse : UnexpectedResponse
raise exception_class.new(response: resp,
object_identifier: object_identifier,
graphql_errors: resp_json['errors'])
end
def not_found_exception?(error)
error['message'] == 'Cocina object not found'
end
end
end
end
end