Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow for extensions of valkyrie indexer's to_solr that depends on resource custom metadata #4463

Merged
merged 2 commits into from
Jul 23, 2020

Conversation

elrayle
Copy link
Contributor

@elrayle elrayle commented Jul 20, 2020

Fixes #4464

Description

Allow shared spec testing of indexers with customizations to #to_solr that depend on properties defined in the resource class that the indexer indexes.

Resolution

Update shared indexers spec to have a valid resource passed in to the shared spec. The shared spec sets values it depends on in a before block.

@samvera/hyrax-code-reviewers

@@ -3,7 +3,8 @@
RSpec.shared_examples 'a Hyrax::Resource indexer' do
subject(:indexer) { indexer_class.new(resource: resource) }
let(:ids) { ['id1', 'id2'] }
let(:resource) { Hyrax::Resource.new(alternate_ids: ids) }
let(:the_resource_class) { defined?(resource_class) ? resource_class : Hyrax::Work }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting resource_class to Hyrax::Work when it isn't defined allows for backward compatibility.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing in the resource_class allows #to_solr to successfully create the solr_doc when the indexer is expecting the resource to have custom metadata defined.

@@ -3,7 +3,12 @@
require 'hyrax/specs/shared_specs'

RSpec.describe Hyrax::ValkyrieCollectionIndexer do
subject(:indexer) { described_class.new(resource: resource) }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The subject is not used anywhere, so it was removed.

subject(:indexer) { described_class.new(resource: resource) }
let(:resource_class) do
Class.new(Hyrax::Resource) do
# include ::Hyrax::CollectionBehavior # TODO: How is this represented in the Valkyrie::Resource version of a Collection?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not find any example of a defined or generated Collection that is a Valkyrie::Resource. The AF version includes CollectionBehavior. I commented it out and left it here as a placeholder for any behaviors that are expected/required for a Valkyrie version of a Hyrax Collection. I'd be interested in what others think should be included in this class. NOTE: Prior to this PR, the Collection indexer was being tested with a Hyrax::Work as the resource passed to the indexer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found it, this should be Hyrax::PcdmCollection

@@ -3,8 +3,7 @@
require 'hyrax/specs/shared_specs'

RSpec.describe Hyrax::ValkyrieWorkIndexer do
let(:indexer) { described_class.new(resource: resource) }
let(:resource) { FactoryBot.valkyrie_create(:hyrax_work) }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indexer and resource were not used by this test, so they were removed.

@@ -21,4 +20,11 @@

it_behaves_like 'a Basic metadata indexer'
end

context 'when extending with custom metadata' do
Copy link
Contributor Author

@elrayle elrayle Jul 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This context is the spec that tests the described behavior of an indexer that uses custom metadata in the resource. Without the fix that allows the resource_class to be passed to the shared spec, this will fail with the Actual Behavior described in the PR comments. With the fix it passes with the Expected Behavior.

@elrayle elrayle changed the title allow for extensions of valkyrie indexer's to_solr that depends on resource custom metadata [WIP] allow for extensions of valkyrie indexer's to_solr that depends on resource custom metadata Jul 20, 2020
let(:resource) { Hyrax::Resource.new(alternate_ids: ids) }
subject(:indexer) { indexer_class.new(resource: the_resource) }
let(:ids) { ['id1', 'id2'] }
let(:the_resource) { defined?(resource) ? resource : Hyrax::Resource.new }
Copy link
Contributor Author

@elrayle elrayle Jul 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is providing backward compatibility. If the caller does not create a resource, then one is created in the shared spec. If backward compatibility is not required, this setup would become...

   subject(:indexer)  { indexer_class.new(resource: resource) }
   let(:ids)          { ['id1', 'id2'] }

NOTE: All references to the_resource are providing backward compatibility for each shared spec. Similar simplifications would occur if resource was required to be defined by the caller.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on conversations, the reason for these conditionals is for backwards compatibility.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this was not released in a Hyrax version, we can remove the conditionals and tighten up the logic. Anyone that's using this as written will have a broken spec.


describe '#to_solr' do
before { the_resource.alternate_ids = ids }
Copy link
Contributor Author

@elrayle elrayle Jul 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sure the alternate_ids are set whether the resource is created in the shared spec or by the caller.

NOTE: Similar use of before blocks or setting in the let(:the_resource) statement in other shared specs to be sure the resource has the values set that are being tested.

@no-reply
Copy link
Contributor

this seems on the right track, in general. it might be simplified by using shared_example parameters as document here: https://relishapp.com/rspec/rspec-core/docs/example-groups/shared-examples

it 'indexes basic metadata' do
expect(indexer.to_solr)
.to include(keyword_sim: a_collection_containing_exactly(*attributes[:keyword]),
.to include(keyword_sim: a_collection_containing_exactly(*attributes[:keyword]),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change just makes the a_collection... align with a_collection... in the following 2 lines. No real code change.

attribute :created, Valkyrie::Types::Date
attribute :isbn, Valkyrie::Types::String
attribute :publisher, Valkyrie::Types::String
attribute :title, Valkyrie::Types::String
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of this is just an alignment change. The only real change is fixing the typo from pubisher to publisher.

…source custom metadata

Fixes #4464

Allow shared spec testing of indexers with customizations to #to_solr that depend on properties defined in the resource class that the indexer indexes.

Update shared indexers spec to have a valid resource passed in to the shared spec.  The shared spec sets values it depends on in a before block.
@@ -5,7 +5,7 @@ class << self
# @param [#id] object - to get the thumbnail for
# @return [String] a path to the thumbnail
def call(object)
return default_image unless object.try(:thumbnail_id)
return default_image unless object.try(:thumbnail_id)&.to_s&.present?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For collections shared spec, the thumbnail_id was a Valkyrie ID with id="". I don't know if this would happen in the application, but it doesn't hurt anything to have the extra code. If thumbnail_id is nil, it will still use the default_image.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be a separate commit. There may be other problems involved. @no-reply can you take action on this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may need to test that Valkyrie::ID with an empty string is not present.

@elrayle
Copy link
Contributor Author

elrayle commented Jul 22, 2020

@no-reply RE: parameters to shared specs... I like the idea of making the parameters more transparent. I don't think it would simplify the code if we still have the requirement of providing a default if the parameter does not have a value. With a change to parameterized shared specs, would you see it as ok to remove the creation a default resource when one is not provided making parameters required? That would simplify the code.

@elrayle
Copy link
Contributor Author

elrayle commented Jul 22, 2020

Problem with parameters for shared specs. You cannot use let or before to do setup on the parameters. They have to be created on the it_behaves_like statement.

WORKS

  it_behaves_like 'a Hyrax::Resource indexer', described_class, Hyrax::Work.new

DOESN'T WORK (error loading spec file)

  let(:indexer_class) { described_class }
  let(:resource) { Hyrax::Work.new }

  it_behaves_like 'a Hyrax::Resource indexer', indexer_class, resource

This example is simple. But if you needed to make a modification to resource before sending it, there isn't a way to do that.

Also, you can't set up the resource once and have it used for all shared specs. You have to copy the creation code to every it_behaves_like statement which isn't DRY.

  it_behaves_like 'a Hyrax::Resource indexer', described_class, Hyrax::Work.new
  it_behaves_like 'a Core metadata indexer', described_class, Hyrax::Work.new
  it_behaves_like 'a permission indexer', described_class, Hyrax::Work.new
  it_behaves_like 'a visibility indexer', described_class, Hyrax::Work.new

Any change to the resource you create requires every it_behaves_like to be updated.

@elrayle elrayle changed the title [WIP] allow for extensions of valkyrie indexer's to_solr that depends on resource custom metadata allow for extensions of valkyrie indexer's to_solr that depends on resource custom metadata Jul 22, 2020
@elrayle
Copy link
Contributor Author

elrayle commented Jul 22, 2020

Example preprocessing from our app. We default our works to public instead of private. So I have to fake this to allow our indexer to pass the visibility indexer shared spec.

  context 'FAKE work defaulting to private visibility for shared specs' do
    before do
      Hyrax::VisibilityWriter.new(resource: resource)
        .assign_access_for(visibility: Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PRIVATE)
    end
    it_behaves_like 'a visibility indexer'
  end

after { Hyrax::Test.send(:remove_const, :Custom) }

let(:resource) { Hyrax.persister.save(resource: Hyrax::Test::Custom::Work.new(broader: ['term1', 'term2'])) }
let(:indexer_class) { Hyrax::Test::Custom::WorkIndexer }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per conversation, this was already supported.

end
after { Hyrax::Test.send(:remove_const, :Custom) }

let(:resource) { Hyrax.persister.save(resource: Hyrax::Test::Custom::Work.new(broader: ['term1', 'term2'])) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per tech call, this is the line that the PR is supporting.

@@ -3,6 +3,7 @@
require 'hyrax/specs/shared_specs'

RSpec.describe Hyrax::ResourceIndexer do
let(:resource) { Hyrax::Resource.new }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is added to clarify and ensure the proper behavior. Prior to this, we had misleading successful test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resource is now required

Copy link
Contributor

@jeremyf jeremyf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -7,6 +7,7 @@ class ValkyrieCollectionIndexer < Hyrax::ValkyrieIndexer
include Hyrax::ResourceIndexer
include Hyrax::PermissionIndexer
include Hyrax::VisibilityIndexer
include Hyrax::Indexer(:core_metadata)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collection resource defines core metadata, so seems like the collection indexer should index it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -5,7 +5,7 @@ class << self
# @param [#id] object - to get the thumbnail for
# @return [String] a path to the thumbnail
def call(object)
return default_image unless object.try(:thumbnail_id)
return default_image unless object.try(:thumbnail_id)&.present?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Catches the case where thumbnail_id is nil and when it is a Valkyrie::ID with an empty string.

@@ -61,9 +82,37 @@
end
end

RSpec.shared_examples 'a Core metadata indexer' do
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is mostly the same with the similar changes for variable validation and removing the default resource. It was moved to make the ordering of the various shared specs easier to follow.

it_behaves_like 'a Hyrax::Resource indexer'
it_behaves_like 'a Core metadata indexer'
it_behaves_like 'a permission indexer'
it_behaves_like 'a visibility indexer'
Copy link
Contributor Author

@elrayle elrayle Jul 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shared spec defines the minimal requirements for a work instead of expecting the individual work tests to know which parts are required.


it_behaves_like 'a Hyrax::Resource indexer'
it_behaves_like 'a Core metadata indexer'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shared spec defines the minimal requirements for a collection instead of expecting the individual collection tests to know which parts are required.

@@ -107,21 +170,3 @@
end
end
end

RSpec.shared_examples 'a Core metadata indexer' do
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved higher in the file

let(:indexer_class) { described_class }

it_behaves_like 'a Hyrax::Resource indexer'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All shared specs representing the minimal required parts of a collection are now defined and tested in the shared spec 'a Collection indexer'.

it_behaves_like 'a Core metadata indexer'
it_behaves_like 'a permission indexer'
it_behaves_like 'a visibility indexer'
it_behaves_like 'a Work indexer'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All shared specs representing the minimal required parts of a work are now defined and tested in the shared spec 'a Work indexer'.

@@ -13,6 +13,7 @@
end

context 'with core metadata schema' do
let(:resource) { work }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resource is required for shared indexer specs

@@ -8,6 +8,7 @@
include Hyrax::PermissionIndexer
end
end
let(:resource) { Hyrax::Resource.new }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resource is required for shared indexer specs

@@ -1,9 +1,19 @@
# frozen_string_literal: true

# These shared specs test various aspects of valkyrie based indexers by calling the #to_solr method.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the docs are really appreciated!

let(:resource) { Hyrax::Resource.new(alternate_ids: ids) }
before do
raise 'indexer_class must be set with `let(:indexer_class)`' unless defined? indexer_class
raise 'resource must be set with `let(:resource)` and is expected to be a kind of Hyrax::Resource' unless defined?(resource) && resource.kind_of?(Hyrax::Resource)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i feel like the type checks might be heavy-handed, but let's keep them for now.

no-reply
no-reply previously approved these changes Jul 22, 2020
no-reply
no-reply previously approved these changes Jul 22, 2020
* adds documentation of usage
* raise exceptions if required indexer_class and resource are not set
* add shared spec for ‘a Work indexer’ that calls shared specs related to works
* add core metadata test to ‘a Collection indexer’
* allow default visibility to be passed in using ‘restricted’ if not passed in
@no-reply no-reply merged commit 88e4731 into master Jul 23, 2020
@no-reply no-reply deleted the indexer_for_resource branch July 23, 2020 00:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

indexer shared specs are not usable outside hyrax when they depend on resource custom fields
3 participants