# 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 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] def collections Collections.new(**parent_params).collections end # Get a list of the members # @raise [UnexpectedResponse] if the request is unsuccessful. # @return [Array] 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