diff --git a/.dassie/.env b/.dassie/.env index a60efa6dad..d0ae43e7a2 100644 --- a/.dassie/.env +++ b/.dassie/.env @@ -18,6 +18,7 @@ HYRAX_ANALYTICS=false HYRAX_ANALYTICS_PROVIDER=google HYRAX_DERIVATIVES_PATH=/app/samvera/hyrax-webapp/derivatives/ HYRAX_ENGINE_PATH=/app/samvera/hyrax-engine +HYRAX_FLEXIBLE=true HYRAX_UPLOAD_PATH=/app/samvera/hyrax-webapp/uploads/ HYRAX_VALKYRIE=true IN_DOCKER=true diff --git a/.dassie/app/indexers/collection_resource_indexer.rb b/.dassie/app/indexers/collection_resource_indexer.rb index c8dd88b84e..76a4ace871 100644 --- a/.dassie/app/indexers/collection_resource_indexer.rb +++ b/.dassie/app/indexers/collection_resource_indexer.rb @@ -3,6 +3,7 @@ # Generated via # `rails generate hyrax:collection_resource CollectionResource` class CollectionResourceIndexer < Hyrax::PcdmCollectionIndexer - include Hyrax::Indexer(:basic_metadata) - include Hyrax::Indexer(:collection_resource) + include Hyrax::Indexer(:basic_metadata) unless Hyrax.config.flexible? + include Hyrax::Indexer(:collection_resource) unless Hyrax.config.flexible? + include Hyrax::Indexer('CollectionResource') if Hyrax.config.flexible? end diff --git a/.dassie/app/indexers/generic_work_resource_indexer.rb b/.dassie/app/indexers/generic_work_resource_indexer.rb index 7598e2a597..6fb90b0083 100644 --- a/.dassie/app/indexers/generic_work_resource_indexer.rb +++ b/.dassie/app/indexers/generic_work_resource_indexer.rb @@ -3,8 +3,9 @@ # Generated via # `rails generate hyrax:work_resource GenericWorkResource` class GenericWorkResourceIndexer < Hyrax::ValkyrieWorkIndexer - include Hyrax::Indexer(:basic_metadata) - include Hyrax::Indexer(:generic_work_resource) + include Hyrax::Indexer(:basic_metadata) unless Hyrax.config.flexible? + include Hyrax::Indexer(:generic_work_resource) unless Hyrax.config.flexible? + include Hyrax::Indexer('GenericWorkResource') if Hyrax.config.flexible? # Uncomment this block if you want to add custom indexing behavior: # def to_solr diff --git a/.dassie/app/indexers/monograph_indexer.rb b/.dassie/app/indexers/monograph_indexer.rb index 73fac4e9a6..092a84a4ba 100644 --- a/.dassie/app/indexers/monograph_indexer.rb +++ b/.dassie/app/indexers/monograph_indexer.rb @@ -3,8 +3,9 @@ # Generated via # `rails generate hyrax:work_resource Monograph` class MonographIndexer < Hyrax::ValkyrieWorkIndexer - include Hyrax::Indexer(:basic_metadata) - include Hyrax::Indexer(:monograph) + include Hyrax::Indexer(:basic_metadata) unless Hyrax.config.flexible? + include Hyrax::Indexer(:monograph) unless Hyrax.config.flexible? + include Hyrax::Indexer('Monograph') if Hyrax.config.flexible? # Uncomment this block if you want to add custom indexing behavior: # def to_solr diff --git a/.dassie/app/models/collection_resource.rb b/.dassie/app/models/collection_resource.rb index f0d140bc3e..1fd97de0e8 100644 --- a/.dassie/app/models/collection_resource.rb +++ b/.dassie/app/models/collection_resource.rb @@ -30,8 +30,8 @@ class CollectionResource < Hyrax::PcdmCollection # * add Valkyrie attributes to this class # * update form and indexer to process the attributes # - include Hyrax::Schema(:basic_metadata) - include Hyrax::Schema(:collection_resource) + include Hyrax::Schema(:basic_metadata) unless Hyrax.config.flexible? + include Hyrax::Schema(:collection_resource) unless Hyrax.config.flexible? Hyrax::ValkyrieLazyMigration.migrating(self, from: ::Collection) if Hyrax.config.valkyrie_transition? end diff --git a/.dassie/app/models/generic_work_resource.rb b/.dassie/app/models/generic_work_resource.rb index 4f034bf821..809d7945e0 100644 --- a/.dassie/app/models/generic_work_resource.rb +++ b/.dassie/app/models/generic_work_resource.rb @@ -3,8 +3,8 @@ # Generated via # `rails generate hyrax:work_resource GenericWorkResource` class GenericWorkResource < Hyrax::Work - include Hyrax::Schema(:basic_metadata) - include Hyrax::Schema(:generic_work_resource) + include Hyrax::Schema(:basic_metadata) unless Hyrax.config.flexible? + include Hyrax::Schema(:generic_work_resource) unless Hyrax.config.flexible? Hyrax::ValkyrieLazyMigration.migrating(self, from: GenericWork) if Hyrax.config.valkyrie_transition? end diff --git a/.dassie/app/models/monograph.rb b/.dassie/app/models/monograph.rb index fc3e67b2a5..34f9b195a4 100644 --- a/.dassie/app/models/monograph.rb +++ b/.dassie/app/models/monograph.rb @@ -3,6 +3,6 @@ # Generated via # `rails generate hyrax:work_resource Monograph` class Monograph < Hyrax::Work - include Hyrax::Schema(:basic_metadata) - include Hyrax::Schema(:monograph) + include Hyrax::Schema(:basic_metadata) unless Hyrax.config.flexible? + include Hyrax::Schema(:monograph) unless Hyrax.config.flexible? end diff --git a/.dassie/config/metadata_profiles/m3_profile.yaml b/.dassie/config/metadata_profiles/m3_profile.yaml new file mode 100644 index 0000000000..c604814d24 --- /dev/null +++ b/.dassie/config/metadata_profiles/m3_profile.yaml @@ -0,0 +1,1036 @@ +--- +m3_version: 1.0.beta2 +profile: + date_modified: '2024-06-01' + responsibility: https://samvera.org + responsibility_statement: Hyrax Initial Profile + type: + version: 1 +classes: + GenericWork: + display_label: Generic Work + GenericWorkResource: + display_label: Generic Work + Wings(Hyrax::Resource): + display_label: Wings Resource + Monograph: + display_label: Monograph + AdminSet: + diplay_label: AdminSet + AdminSetResource: + diplay_label: AdministrativeSet + Collection: + display_label: Collection + CollectionResource: + display_label: PcdmCollection + Hyrax::FileSet: + display_label: FileSet +contexts: + flexible_context: + display_label: Flexible Metadata Example +mappings: + blacklight: + name: Additional Blacklight Solr Mappings + metatags: + name: Metatags + mods_oai_pmh: + name: MODS OAI PMH + qualified_dc_pmh: + name: Qualified DC OAI PMH + simple_dc_pmh: + name: Simple DC OAI PMH +properties: + title: + available_on: + class: + - AdminSet + - AdminSetResource + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + cardinality: + minimum: 1 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + definition: + default: Enter a standardized title for display. If only one + title is needed, transcribe the title from the source + itself. + display_label: + default: Title + index_documentation: displayable, searchable + indexing: + - 'title_sim' + - 'title_tesim' + form: + required: true + primary: true + multi_value: true + mappings: + metatags: twitter:title, og:title + mods_oai_pmh: mods:titleInfo/mods:title + qualified_dc_pmh: dcterms:title + simple_dc_pmh: dc:title + property_uri: http://purl.org/dc/terms/title + range: http://www.w3.org/2001/XMLSchema#string + requirement: required + sample_values: + - Pencil drawn portrait study of woman + date_modified: + available_on: + class: + - AdminSet + - AdminSetResource + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + display_label: + default: Date Modified + property_uri: http://purl.org/dc/terms/modified + range: http://www.w3.org/2001/XMLSchema#dateTime + sample_values: + - "2024-06-06 21:06:51 +0000" + date_uploaded: + available_on: + class: + - AdminSet + - AdminSetResource + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + display_label: + default: Date Uploaded + property_uri: http://purl.org/dc/terms/dateSubmitted + range: http://www.w3.org/2001/XMLSchema#dateTime + sample_values: + - "2024-06-06 21:06:51 +0000" + depositor: + available_on: + class: + - AdminSet + - AdminSetResource + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Depositor + index_documentation: searchable + indexing: + - 'depositor_tesim' + property_uri: http://id.loc.gov/vocabulary/relators/dpt + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Julie Allinson + creator: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 1 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Creator + index_documentation: displayable, searchable + indexing: + - 'creator_sim' + - 'creator_tesim' + form: + required: true + primary: true + property_uri: http://purl.org/dc/elements/1.1/creator + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Julie Allinson + license: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: License + index_documentation: displayable, searchable + indexing: + - 'license_sim' + - 'license_tesim' + form: + primary: false + property_uri: http://purl.org/dc/terms/license + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - http://creativecommons.org/licenses/by/3.0/us/ + abstract: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Abstract + index_documentation: displayable, searchable + indexing: + - 'abstract_sim' + - 'abstract_tesim' + form: + primary: false + property_uri: http://purl.org/dc/terms/abstract + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - This is an abstract. + access_right: + available_on: + class: + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Access Right + index_documentation: displayable, searchable + indexing: + - 'access_right_sim' + - 'access_right_tesim' + form: + primary: false + property_uri: http://purl.org/dc/terms/accessRights + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Open Access + alternative_title: + available_on: + class: + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Alternative Title + index_documentation: displayable, searchable + indexing: + - 'alternative_title_sim' + - 'alternative_title_tesim' + form: + primary: false + property_uri: http://purl.org/dc/terms/alternative + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Alternate Title Example + arkivo_checksum: + available_on: + class: + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Arkivo Checksum + form: + primary: false + property_uri: http://scholarsphere.psu.edu/ns#arkivoChecksum + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - c0855931b7f1aefedb91d31af76873c0 + based_near: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Location + index_documentation: displayable, searchable + indexing: + - 'based_near_sim' + - 'based_near_tesim' + form: + primary: false + property_uri: http://xmlns.com/foaf/0.1/based_near + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - San Diego, California, United States + bibliographic_citation: + available_on: + class: + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Bibliographic Citation + index_documentation: displayable, searchable + indexing: + - 'bibliographic_citation_sim' + - 'bibliographic_citation_tesim' + form: + primary: false + property_uri: http://purl.org/dc/terms/bibliographicCitation + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - "Doe, J. (2024). Example Citation. Journal of Examples, 12(3), 45-67." + contributor: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Contributor + index_documentation: displayable, searchable + indexing: + - 'contributor_tesim' + - 'contributor_sim' + form: + primary: false + property_uri: http://purl.org/dc/elements/1.1/contributor + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Julie Allinson + date_created: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#dateTime + sources: + - 'null' + display_label: + default: Date Created + index_documentation: displayable, searchable + indexing: + - 'date_created_sim' + - 'date_created_tesim' + form: + primary: false + property_uri: http://purl.org/dc/terms/created + range: http://www.w3.org/2001/XMLSchema#dateTime + sample_values: + sample_values: + - "2024-06-06 21:06:51 +0000" + description: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Description + index_documentation: displayable, searchable + indexing: + - 'description_sim' + - 'description_tesim' + form: + primary: false + property_uri: http://purl.org/dc/elements/1.1/description + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - This is a description. + identifier: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Identifier + index_documentation: displayable, searchable + indexing: + - 'identifier_sim' + - 'identifier_tesim' + form: + primary: false + property_uri: http://purl.org/dc/terms/identifier + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - abc123 + import_url: + available_on: + class: + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Import URL + property_uri: http://scholarsphere.psu.edu/ns#importUrl + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - http://example.com/resource1 + keyword: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Keyword + index_documentation: displayable, searchable + indexing: + - 'keyword_sim' + - 'keyword_tesim' + form: + primary: false + property_uri: http://schema.org/keywords + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Metadata + - Repository + label: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Label + index_documentation: displayable, searchable + indexing: + - 'label_sim' + - 'label_tesim' + form: + primary: false + property_uri: info:fedora/fedora-system:def/model#downloadFilename + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - file_label.txt + language: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Language + index_documentation: displayable, searchable + indexing: + - 'language_sim' + - 'language_tesim' + form: + primary: false + property_uri: http://purl.org/dc/elements/1.1/language + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - English + - Spanish + publisher: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Publisher + index_documentation: displayable, searchable + indexing: + - 'publisher_sim' + - 'publisher_tesim' + form: + primary: false + property_uri: http://purl.org/dc/elements/1.1/publisher + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Scholastic + related_url: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Related URL + index_documentation: displayable, searchable + indexing: + - 'related_url_sim' + - 'related_url_tesim' + form: + primary: false + property_uri: http://www.w3.org/2000/01/rdf-schema#seeAlso + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - http://example.com/resource1 + relative_path: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Relative Path + property_uri: http://scholarsphere.psu.edu/ns#relativePath + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - /path/to/resource + resource_type: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Resource Type + index_documentation: displayable, searchable + indexing: + - 'resource_type_sim' + - 'resource_type_tesim' + form: + primary: false + property_uri: http://purl.org/dc/terms/type + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Article + - Conference Proceeding + rights_notes: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Rights Notes + index_documentation: displayable, searchable + indexing: + - 'rights_notes_sim' + - 'rights_notes_tesim' + form: + primary: false + property_uri: http://purl.org/dc/elements/1.1/rights + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Creative Commons license. + rights_statement: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Rights Statement + index_documentation: displayable, searchable + indexing: + - 'rights_statement_sim' + - 'rights_statement_tesim' + form: + primary: true + property_uri: http://www.europeana.eu/schemas/edm/rights + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - http://rightsstatements.org/vocab/InC/1.0/ + source: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Source + index_documentation: displayable, searchable + indexing: + - 'source_sim' + - 'source_tesim' + form: + primary: false + property_uri: http://purl.org/dc/terms/source + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Original Manuscript + - Digital Archive + subject: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Wings(Hyrax::Resource) + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Subject + index_documentation: displayable, searchable + indexing: + - 'subject_sim' + - 'subject_tesim' + form: + primary: false + property_uri: http://purl.org/dc/elements/1.1/subject + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Art + - Science + target_audience: + available_on: + class: + - CollectionResource + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Target Audience + form: + primary: true + multiple: true + property_uri: http://hyrax-example.com/target_audience + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Researchers + - Students + department: + available_on: + class: + - CollectionResource + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Department + form: + primary: true + property_uri: http://hyrax-example.com/department + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Mathematics + - History + course: + available_on: + class: + - CollectionResource + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Course + form: + primary: false + property_uri: http://hyrax-example.com/course + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Computer Science 50 + monograph_title: + available_on: + class: + - Monograph + cardinality: + minimum: 1 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Monograph Title + property_uri: http://hyrax-example.com/monograph_title + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Example Monograph Title + record_info: + available_on: + class: + - Monograph + cardinality: + minimum: 1 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Record Info + index_documentation: searchable + indexing: + - 'record_info_tesim' + form: + required: true + primary: true + property_uri: http://hyrax-example.com/record_info + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Example Record Info + place_of_publication: + available_on: + class: + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Place of Publication + form: + required: false + primary: true + property_uri: http://hyrax-example.com/place_of_publication + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Example Place of Publication + genre: + available_on: + class: + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Genre + form: + primary: true + property_uri: http://hyrax-example.com/genre + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Fiction + - Non-Fiction + series_title: + available_on: + class: + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Series Title + form: + primary: false + property_uri: http://hyrax-example.com/series_title + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Example Series Title + target_audience: + available_on: + class: + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Target Audience + form: + multiple: true + property_uri: http://hyrax-example.com/target_audience + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Researchers + - Students + table_of_contents: + available_on: + class: + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Table of Contents + form: + multiple: false + property_uri: http://hyrax-example.com/table_of_contents + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Example Table of Contents + date_of_issuance: + available_on: + class: + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Date of Issuance + property_uri: http://hyrax-example.com/date_of_issuance + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - "2024-06-06 21:06:51 +0000" diff --git a/.dassie/db/migrate/20240606205215_create_hyrax_flexible_schemas.rb b/.dassie/db/migrate/20240606205215_create_hyrax_flexible_schemas.rb new file mode 100644 index 0000000000..bdbac2bc49 --- /dev/null +++ b/.dassie/db/migrate/20240606205215_create_hyrax_flexible_schemas.rb @@ -0,0 +1,9 @@ +class CreateHyraxFlexibleSchemas < ActiveRecord::Migration[6.1] + def change + create_table :hyrax_flexible_schemas do |t| + t.text :profile + + t.timestamps + end + end +end diff --git a/.dassie/db/schema.rb b/.dassie/db/schema.rb index 1515b687c5..639068a227 100644 --- a/.dassie/db/schema.rb +++ b/.dassie/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2024_05_06_070809) do +ActiveRecord::Schema.define(version: 2024_06_06_205215) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -172,6 +172,12 @@ t.datetime "updated_at", null: false end + create_table "hyrax_flexible_schemas", force: :cascade do |t| + t.text "profile" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + end + create_table "job_io_wrappers", force: :cascade do |t| t.bigint "user_id" t.bigint "uploaded_file_id" diff --git a/.koppie/app/indexers/collection_resource_indexer.rb b/.koppie/app/indexers/collection_resource_indexer.rb index c8dd88b84e..76a4ace871 100644 --- a/.koppie/app/indexers/collection_resource_indexer.rb +++ b/.koppie/app/indexers/collection_resource_indexer.rb @@ -3,6 +3,7 @@ # Generated via # `rails generate hyrax:collection_resource CollectionResource` class CollectionResourceIndexer < Hyrax::PcdmCollectionIndexer - include Hyrax::Indexer(:basic_metadata) - include Hyrax::Indexer(:collection_resource) + include Hyrax::Indexer(:basic_metadata) unless Hyrax.config.flexible? + include Hyrax::Indexer(:collection_resource) unless Hyrax.config.flexible? + include Hyrax::Indexer('CollectionResource') if Hyrax.config.flexible? end diff --git a/.koppie/app/indexers/generic_work_indexer.rb b/.koppie/app/indexers/generic_work_indexer.rb index c71c28b6b3..27345aecaf 100644 --- a/.koppie/app/indexers/generic_work_indexer.rb +++ b/.koppie/app/indexers/generic_work_indexer.rb @@ -3,8 +3,9 @@ # Generated via # `rails generate hyrax:work_resource GenericWork` class GenericWorkIndexer < Hyrax::ValkyrieWorkIndexer - include Hyrax::Indexer(:basic_metadata) - include Hyrax::Indexer(:generic_work) + include Hyrax::Indexer(:basic_metadata) unless Hyrax.config.flexible? + include Hyrax::Indexer(:generic_work) unless Hyrax.config.flexible? + include Hyrax::Indexer('GenericWork') if Hyrax.config.flexible? # Uncomment this block if you want to add custom indexing behavior: # def to_solr diff --git a/.koppie/app/indexers/monograph_indexer.rb b/.koppie/app/indexers/monograph_indexer.rb index 73fac4e9a6..092a84a4ba 100644 --- a/.koppie/app/indexers/monograph_indexer.rb +++ b/.koppie/app/indexers/monograph_indexer.rb @@ -3,8 +3,9 @@ # Generated via # `rails generate hyrax:work_resource Monograph` class MonographIndexer < Hyrax::ValkyrieWorkIndexer - include Hyrax::Indexer(:basic_metadata) - include Hyrax::Indexer(:monograph) + include Hyrax::Indexer(:basic_metadata) unless Hyrax.config.flexible? + include Hyrax::Indexer(:monograph) unless Hyrax.config.flexible? + include Hyrax::Indexer('Monograph') if Hyrax.config.flexible? # Uncomment this block if you want to add custom indexing behavior: # def to_solr diff --git a/.koppie/app/models/collection_resource.rb b/.koppie/app/models/collection_resource.rb index 8ba57bc859..435cdf595f 100644 --- a/.koppie/app/models/collection_resource.rb +++ b/.koppie/app/models/collection_resource.rb @@ -27,6 +27,6 @@ class CollectionResource < Hyrax::PcdmCollection # * add Valkyrie attributes to this class # * update form and indexer to process the attributes # - include Hyrax::Schema(:basic_metadata) - include Hyrax::Schema(:collection_resource) + include Hyrax::Schema(:basic_metadata) unless Hyrax.config.flexible? + include Hyrax::Schema(:collection_resource) unless Hyrax.config.flexible? end diff --git a/.koppie/app/models/generic_work.rb b/.koppie/app/models/generic_work.rb index d884387774..0f3debda26 100644 --- a/.koppie/app/models/generic_work.rb +++ b/.koppie/app/models/generic_work.rb @@ -3,6 +3,6 @@ # Generated via # `rails generate hyrax:work_resource GenericWork` class GenericWork < Hyrax::Work - include Hyrax::Schema(:basic_metadata) - include Hyrax::Schema(:generic_work) + include Hyrax::Schema(:basic_metadata) unless Hyrax.config.flexible? + include Hyrax::Schema(:generic_work) unless Hyrax.config.flexible? end diff --git a/.koppie/app/models/monograph.rb b/.koppie/app/models/monograph.rb index fc3e67b2a5..34f9b195a4 100644 --- a/.koppie/app/models/monograph.rb +++ b/.koppie/app/models/monograph.rb @@ -3,6 +3,6 @@ # Generated via # `rails generate hyrax:work_resource Monograph` class Monograph < Hyrax::Work - include Hyrax::Schema(:basic_metadata) - include Hyrax::Schema(:monograph) + include Hyrax::Schema(:basic_metadata) unless Hyrax.config.flexible? + include Hyrax::Schema(:monograph) unless Hyrax.config.flexible? end diff --git a/.koppie/config/metadata_profiles/m3_profile.yaml b/.koppie/config/metadata_profiles/m3_profile.yaml new file mode 100644 index 0000000000..39fe059a67 --- /dev/null +++ b/.koppie/config/metadata_profiles/m3_profile.yaml @@ -0,0 +1,968 @@ +--- +m3_version: 1.0.beta2 +profile: + date_modified: '2024-06-01' + responsibility: https://samvera.org + responsibility_statement: Hyrax Initial Profile + type: + version: 1 +classes: + GenericWork: + display_label: Generic Work + Monograph: + display_label: Monograph + Hyrax::AdministrativeSet: + diplay_label: AdministrativeSet + CollectionResource: + display_label: PcdmCollection + Hyrax::FileSet: + display_label: FileSet +contexts: + flexible_context: + display_label: Flexible Metadata Example +mappings: + blacklight: + name: Additional Blacklight Solr Mappings + metatags: + name: Metatags + mods_oai_pmh: + name: MODS OAI PMH + qualified_dc_pmh: + name: Qualified DC OAI PMH + simple_dc_pmh: + name: Simple DC OAI PMH +properties: + title: + available_on: + class: + - Hyrax::AdministrativeSet + - Hyrax::FileSet + - CollectionResource + - GenericWork + cardinality: + minimum: 1 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + definition: + default: Enter a standardized title for display. If only one + title is needed, transcribe the title from the source + itself. + display_label: + default: Title + index_documentation: displayable, searchable + indexing: + - 'title_sim' + - 'title_tesim' + form: + required: true + primary: true + multi_value: true + mappings: + metatags: twitter:title, og:title + mods_oai_pmh: mods:titleInfo/mods:title + qualified_dc_pmh: dcterms:title + simple_dc_pmh: dc:title + property_uri: http://purl.org/dc/terms/title + range: http://www.w3.org/2001/XMLSchema#string + requirement: required + sample_values: + - Pencil drawn portrait study of woman + date_modified: + available_on: + class: + - Hyrax::AdministrativeSet + - Hyrax::FileSet + - CollectionResource + - GenericWork + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + display_label: + default: Date Modified + property_uri: http://purl.org/dc/terms/modified + range: http://www.w3.org/2001/XMLSchema#dateTime + sample_values: + - "2024-06-06 21:06:51 +0000" + date_uploaded: + available_on: + class: + - Hyrax::AdministrativeSet + - Hyrax::FileSet + - CollectionResource + - GenericWork + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + display_label: + default: Date Uploaded + property_uri: http://purl.org/dc/terms/dateSubmitted + range: http://www.w3.org/2001/XMLSchema#dateTime + sample_values: + - "2024-06-06 21:06:51 +0000" + depositor: + available_on: + class: + - Hyrax::AdministrativeSet + - Hyrax::FileSet + - CollectionResource + - GenericWork + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Depositor + index_documentation: searchable + indexing: + - 'depositor_tesim' + property_uri: http://id.loc.gov/vocabulary/relators/dpt + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Julie Allinson + creator: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 1 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Creator + index_documentation: displayable, searchable + indexing: + - 'creator_sim' + - 'creator_tesim' + form: + required: true + primary: true + property_uri: http://purl.org/dc/elements/1.1/creator + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Julie Allinson + license: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: License + index_documentation: displayable, searchable + indexing: + - 'license_sim' + - 'license_tesim' + form: + primary: false + property_uri: http://purl.org/dc/terms/license + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - http://creativecommons.org/licenses/by/3.0/us/ + abstract: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Abstract + index_documentation: displayable, searchable + indexing: + - 'abstract_sim' + - 'abstract_tesim' + form: + primary: false + property_uri: http://purl.org/dc/terms/abstract + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - This is an abstract. + access_right: + available_on: + class: + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Access Right + index_documentation: displayable, searchable + indexing: + - 'access_right_sim' + - 'access_right_tesim' + form: + primary: false + property_uri: http://purl.org/dc/terms/accessRights + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Open Access + alternative_title: + available_on: + class: + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Alternative Title + index_documentation: displayable, searchable + indexing: + - 'alternative_title_sim' + - 'alternative_title_tesim' + form: + primary: false + property_uri: http://purl.org/dc/terms/alternative + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Alternate Title Example + arkivo_checksum: + available_on: + class: + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Arkivo Checksum + form: + primary: false + property_uri: http://scholarsphere.psu.edu/ns#arkivoChecksum + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - c0855931b7f1aefedb91d31af76873c0 + based_near: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Location + index_documentation: displayable, searchable + indexing: + - 'based_near_sim' + - 'based_near_tesim' + form: + primary: false + property_uri: http://xmlns.com/foaf/0.1/based_near + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - San Diego, California, United States + bibliographic_citation: + available_on: + class: + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Bibliographic Citation + index_documentation: displayable, searchable + indexing: + - 'bibliographic_citation_sim' + - 'bibliographic_citation_tesim' + form: + primary: false + property_uri: http://purl.org/dc/terms/bibliographicCitation + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - "Doe, J. (2024). Example Citation. Journal of Examples, 12(3), 45-67." + contributor: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Contributor + index_documentation: displayable, searchable + indexing: + - 'contributor_tesim' + - 'contributor_sim' + form: + primary: false + property_uri: http://purl.org/dc/elements/1.1/contributor + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Julie Allinson + date_created: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#dateTime + sources: + - 'null' + display_label: + default: Date Created + index_documentation: displayable, searchable + indexing: + - 'date_created_sim' + - 'date_created_tesim' + form: + primary: false + property_uri: http://purl.org/dc/terms/created + range: http://www.w3.org/2001/XMLSchema#dateTime + sample_values: + sample_values: + - "2024-06-06 21:06:51 +0000" + description: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Description + index_documentation: displayable, searchable + indexing: + - 'description_sim' + - 'description_tesim' + form: + primary: false + property_uri: http://purl.org/dc/elements/1.1/description + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - This is a description. + identifier: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Identifier + index_documentation: displayable, searchable + indexing: + - 'identifier_sim' + - 'identifier_tesim' + form: + primary: false + property_uri: http://purl.org/dc/terms/identifier + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - abc123 + import_url: + available_on: + class: + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Import URL + property_uri: http://scholarsphere.psu.edu/ns#importUrl + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - http://example.com/resource1 + keyword: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Keyword + index_documentation: displayable, searchable + indexing: + - 'keyword_sim' + - 'keyword_tesim' + form: + primary: false + property_uri: http://schema.org/keywords + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Metadata + - Repository + label: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Label + index_documentation: displayable, searchable + indexing: + - 'label_sim' + - 'label_tesim' + form: + primary: false + property_uri: info:fedora/fedora-system:def/model#downloadFilename + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - file_label.txt + language: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Language + index_documentation: displayable, searchable + indexing: + - 'language_sim' + - 'language_tesim' + form: + primary: false + property_uri: http://purl.org/dc/elements/1.1/language + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - English + - Spanish + publisher: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Publisher + index_documentation: displayable, searchable + indexing: + - 'publisher_sim' + - 'publisher_tesim' + form: + primary: false + property_uri: http://purl.org/dc/elements/1.1/publisher + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Scholastic + related_url: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Related URL + index_documentation: displayable, searchable + indexing: + - 'related_url_sim' + - 'related_url_tesim' + form: + primary: false + property_uri: http://www.w3.org/2000/01/rdf-schema#seeAlso + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - http://example.com/resource1 + relative_path: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Relative Path + property_uri: http://scholarsphere.psu.edu/ns#relativePath + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - /path/to/resource + resource_type: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Resource Type + index_documentation: displayable, searchable + indexing: + - 'resource_type_sim' + - 'resource_type_tesim' + form: + primary: false + property_uri: http://purl.org/dc/terms/type + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Article + - Conference Proceeding + rights_notes: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Rights Notes + index_documentation: displayable, searchable + indexing: + - 'rights_notes_sim' + - 'rights_notes_tesim' + form: + primary: false + property_uri: http://purl.org/dc/elements/1.1/rights + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Creative Commons license. + rights_statement: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Rights Statement + index_documentation: displayable, searchable + indexing: + - 'rights_statement_sim' + - 'rights_statement_tesim' + form: + primary: true + property_uri: http://www.europeana.eu/schemas/edm/rights + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - http://rightsstatements.org/vocab/InC/1.0/ + source: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Source + index_documentation: displayable, searchable + indexing: + - 'source_sim' + - 'source_tesim' + form: + primary: false + property_uri: http://purl.org/dc/terms/source + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Original Manuscript + - Digital Archive + subject: + available_on: + class: + - Hyrax::FileSet + - CollectionResource + - GenericWork + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Subject + index_documentation: displayable, searchable + indexing: + - 'subject_sim' + - 'subject_tesim' + form: + primary: false + property_uri: http://purl.org/dc/elements/1.1/subject + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Art + - Science + target_audience: + available_on: + class: + - CollectionResource + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Target Audience + form: + primary: true + multiple: true + property_uri: http://hyrax-example.com/target_audience + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Researchers + - Students + department: + available_on: + class: + - CollectionResource + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Department + form: + primary: true + property_uri: http://hyrax-example.com/department + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Mathematics + - History + course: + available_on: + class: + - CollectionResource + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Course + form: + primary: false + property_uri: http://hyrax-example.com/course + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Computer Science 50 + monograph_title: + available_on: + class: + - Monograph + cardinality: + minimum: 1 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Monograph Title + property_uri: http://hyrax-example.com/monograph_title + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Example Monograph Title + record_info: + available_on: + class: + - Monograph + cardinality: + minimum: 1 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Record Info + index_documentation: searchable + indexing: + - 'record_info_tesim' + form: + required: true + primary: true + property_uri: http://hyrax-example.com/record_info + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Example Record Info + place_of_publication: + available_on: + class: + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Place of Publication + form: + required: false + primary: true + property_uri: http://hyrax-example.com/place_of_publication + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Example Place of Publication + genre: + available_on: + class: + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Genre + form: + primary: true + property_uri: http://hyrax-example.com/genre + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Fiction + - Non-Fiction + series_title: + available_on: + class: + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Series Title + form: + primary: false + property_uri: http://hyrax-example.com/series_title + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Example Series Title + target_audience: + available_on: + class: + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Target Audience + form: + multiple: true + property_uri: http://hyrax-example.com/target_audience + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Researchers + - Students + table_of_contents: + available_on: + class: + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Table of Contents + form: + multiple: false + property_uri: http://hyrax-example.com/table_of_contents + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Example Table of Contents + date_of_issuance: + available_on: + class: + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Date of Issuance + property_uri: http://hyrax-example.com/date_of_issuance + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - "2024-06-06 21:06:51 +0000" diff --git a/.koppie/db/migrate/20240606205215_create_hyrax_flexible_schemas.rb b/.koppie/db/migrate/20240606205215_create_hyrax_flexible_schemas.rb new file mode 100644 index 0000000000..bdbac2bc49 --- /dev/null +++ b/.koppie/db/migrate/20240606205215_create_hyrax_flexible_schemas.rb @@ -0,0 +1,9 @@ +class CreateHyraxFlexibleSchemas < ActiveRecord::Migration[6.1] + def change + create_table :hyrax_flexible_schemas do |t| + t.text :profile + + t.timestamps + end + end +end diff --git a/.koppie/db/schema.rb b/.koppie/db/schema.rb index 90c60c8384..c4dfd4eafa 100644 --- a/.koppie/db/schema.rb +++ b/.koppie/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2023_08_03_165135) do +ActiveRecord::Schema.define(version: 2024_06_06_205215) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -164,6 +164,12 @@ t.datetime "updated_at", null: false end + create_table "hyrax_flexible_schemas", force: :cascade do |t| + t.text "profile" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + end + create_table "job_io_wrappers", force: :cascade do |t| t.bigint "user_id" t.bigint "uploaded_file_id" diff --git a/app/indexers/hyrax/indexers/administrative_set_indexer.rb b/app/indexers/hyrax/indexers/administrative_set_indexer.rb index 787cbe7e4e..5c69a8e589 100644 --- a/app/indexers/hyrax/indexers/administrative_set_indexer.rb +++ b/app/indexers/hyrax/indexers/administrative_set_indexer.rb @@ -7,7 +7,8 @@ module Indexers class AdministrativeSetIndexer < Hyrax::Indexers::ResourceIndexer include Hyrax::PermissionIndexer include Hyrax::VisibilityIndexer - include Hyrax::Indexer(:core_metadata) + include Hyrax::Indexer(:core_metadata) unless Hyrax.config.flexible? + include Hyrax::Indexer('Hyrax::AdministrativeSet') if Hyrax.config.flexible? def to_solr # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength super.tap do |solr_doc| diff --git a/app/indexers/hyrax/indexers/file_set_indexer.rb b/app/indexers/hyrax/indexers/file_set_indexer.rb index b684753025..5862e2d32a 100644 --- a/app/indexers/hyrax/indexers/file_set_indexer.rb +++ b/app/indexers/hyrax/indexers/file_set_indexer.rb @@ -8,8 +8,9 @@ class FileSetIndexer < Hyrax::Indexers::ResourceIndexer # rubocop:disable Metric include Hyrax::PermissionIndexer include Hyrax::VisibilityIndexer include Hyrax::ThumbnailIndexer - include Hyrax::Indexer(:core_metadata) - include Hyrax::Indexer(:file_set_metadata) + include Hyrax::Indexer(:core_metadata) unless Hyrax.config.flexible? + include Hyrax::Indexer(:file_set_metadata) unless Hyrax.config.flexible? + include Hyrax::Indexer('Hyrax::FileSet') if Hyrax.config.flexible? def to_solr # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity super.tap do |solr_doc| # rubocop:disable Metrics/BlockLength diff --git a/app/indexers/hyrax/indexers/pcdm_collection_indexer.rb b/app/indexers/hyrax/indexers/pcdm_collection_indexer.rb index a146bc4d13..deb9e52338 100644 --- a/app/indexers/hyrax/indexers/pcdm_collection_indexer.rb +++ b/app/indexers/hyrax/indexers/pcdm_collection_indexer.rb @@ -9,7 +9,8 @@ class PcdmCollectionIndexer < Hyrax::Indexers::ResourceIndexer include Hyrax::VisibilityIndexer include Hyrax::LocationIndexer include Hyrax::ThumbnailIndexer - include Hyrax::Indexer(:core_metadata) + include Hyrax::Indexer(:core_metadata) unless Hyrax.config.flexible? + include Hyrax::Indexer('Hyrax::PcdmCollection') if Hyrax.config.flexible? self.thumbnail_path_service = CollectionThumbnailPathService diff --git a/app/indexers/hyrax/indexers/pcdm_object_indexer.rb b/app/indexers/hyrax/indexers/pcdm_object_indexer.rb index 8451a7d59d..3dcfc209f9 100644 --- a/app/indexers/hyrax/indexers/pcdm_object_indexer.rb +++ b/app/indexers/hyrax/indexers/pcdm_object_indexer.rb @@ -9,7 +9,7 @@ class PcdmObjectIndexer < Hyrax::Indexers::ResourceIndexer include Hyrax::VisibilityIndexer include Hyrax::LocationIndexer include Hyrax::ThumbnailIndexer - include Hyrax::Indexer(:core_metadata) + include Hyrax::Indexer(:core_metadata) unless Hyrax.config.flexible? def to_solr # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength super.tap do |solr_doc| @@ -20,13 +20,13 @@ def to_solr # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Met solr_doc['admin_set_sim'] = admin_set_label solr_doc['admin_set_tesim'] = admin_set_label solr_doc["#{Hyrax.config.admin_set_predicate.qname.last}_ssim"] = [resource.admin_set_id.to_s] - solr_doc['member_of_collection_ids_ssim'] = resource.member_of_collection_ids.map(&:to_s) - solr_doc['member_ids_ssim'] = resource.member_ids.map(&:to_s) + solr_doc['member_of_collection_ids_ssim'] = resource.member_of_collection_ids&.map(&:to_s) + solr_doc['member_ids_ssim'] = resource.member_ids&.map(&:to_s) solr_doc['depositor_ssim'] = [resource.depositor] solr_doc['depositor_tesim'] = [resource.depositor] solr_doc['hasRelatedMediaFragment_ssim'] = [resource.representative_id.to_s] solr_doc['hasRelatedImage_ssim'] = [resource.thumbnail_id.to_s] - solr_doc['hasFormat_ssim'] = resource.rendering_ids.map(&:to_s) if resource.rendering_ids.present? + solr_doc['hasFormat_ssim'] = resource.rendering_ids&.map(&:to_s) if resource.rendering_ids.present? index_embargo(solr_doc) index_lease(solr_doc) end diff --git a/app/models/concerns/hyrax/flexibility.rb b/app/models/concerns/hyrax/flexibility.rb new file mode 100644 index 0000000000..1f96672f5b --- /dev/null +++ b/app/models/concerns/hyrax/flexibility.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +module Hyrax + module Flexibility + extend ActiveSupport::Concern + included do + attribute :schema_version, Valkyrie::Types::String + end + + class_methods do + ## Override dry-struct 1.6.0 to enable redefining schemas on the fly + def attributes(new_schema) + keys = new_schema.keys.map { |k| k.to_s.chomp("?").to_sym } + schema_location = singleton_class? ? self.superclass : self + schema schema_location.schema.schema(new_schema) + + define_accessors(keys) + + @attribute_names = nil + + direct_descendants = descendants&.select { |d| d.superclass == self } + direct_descendants&.each do |d| + inherited_attrs = new_schema.reject { |k, _| d.has_attribute?(k.to_s.chomp("?").to_sym) } + d.attributes(inherited_attrs) + end + + new_schema.each_key do |key| + key = key.to_s.chomp('?') + next if instance_methods.include?("#{key}=".to_sym) + + class_eval(<<-RUBY) + def #{key}=(value) + set_value("#{key}".to_sym, value) + end + RUBY + end + + self + end + + ## Override dry-struct 1.6.0 to filter attributes after schema reload happens + def new(attributes = default_attributes, safe = false, &block) # rubocop:disable Style/OptionalBooleanParameter + if attributes.is_a?(Struct) + if equal?(attributes.class) + attributes + else + # This implicit coercion is arguable but makes sense overall + # in cases there you pass child struct to the base struct constructor + # User.new(super_user) + # + # We may deprecate this behavior in future forcing people to be explicit + new(attributes.to_h, safe, &block) + end + else + load(attributes, safe) + end + rescue Dry::Types::CoercionError => e + raise Dry::Error, "[#{self}.new] #{e}", e.backtrace + end + + ## Read the schema from the database and load the correct schemas for the instance in to the class + def load(attributes, safe = false) + attributes[:schema_version] ||= Hyrax::FlexibleSchema.order('id DESC').pick(:id) + struct = allocate + schema_version = attributes[:schema_version] + struct.singleton_class.attributes(Hyrax::Schema(self, schema_version:).attributes) + clean_attributes = safe ? struct.singleton_class.schema.call_safe(attributes) { |output = attributes| return yield output } : struct.singleton_class.schema.call_unsafe(attributes) + struct.__send__(:initialize, clean_attributes) + struct + end + end + + # Override set_value from valkyrie 3.1.1 to enable dynamic schema loading + def set_value(key, value) + @attributes[key.to_sym] = self.singleton_class.schema.key(key.to_sym).type.call(value) + end + + # Override inspect from dry-struct 1.6.0 to enable dynamic schema loading + def inspect + klass = self.singleton_class + attrs = klass.attribute_names.map { |key| " #{key}=#{@attributes[key].inspect}" }.join + "#<#{klass.name || klass.inspect}#{attrs}>" + end + end +end diff --git a/app/models/hyrax/administrative_set.rb b/app/models/hyrax/administrative_set.rb index 0b82afa7e4..325976a548 100644 --- a/app/models/hyrax/administrative_set.rb +++ b/app/models/hyrax/administrative_set.rb @@ -25,7 +25,7 @@ module Hyrax # @see Valkyrie query adapter's #find_inverse_references_by # class AdministrativeSet < Hyrax::Resource - include Hyrax::Schema(:core_metadata) + include Hyrax::Schema(:core_metadata) unless Hyrax.config.flexible? attribute :alternative_title, Valkyrie::Types::Set.of(Valkyrie::Types::String) attribute :creator, Valkyrie::Types::Set.of(Valkyrie::Types::String) diff --git a/app/models/hyrax/file_set.rb b/app/models/hyrax/file_set.rb index 2a4dfc1cec..1df39ca589 100644 --- a/app/models/hyrax/file_set.rb +++ b/app/models/hyrax/file_set.rb @@ -49,8 +49,8 @@ module Hyrax # @see Hyrax::CustomQueries::Navigators::ParentWorkNavigator#find_parent_work # @see https://wiki.duraspace.org/display/samvera/Hydra%3A%3AWorks+Shared+Modeling class FileSet < Hyrax::Resource - include Hyrax::Schema(:core_metadata) - include Hyrax::Schema(:file_set_metadata) + include Hyrax::Schema(:core_metadata) unless Hyrax.config.flexible? + include Hyrax::Schema(:file_set_metadata) unless Hyrax.config.flexible? def self.model_name(name_class: Hyrax::Name) @_model_name ||= name_class.new(self, nil, 'FileSet') diff --git a/app/models/hyrax/flexible_schema.rb b/app/models/hyrax/flexible_schema.rb new file mode 100644 index 0000000000..e0cef5eb2b --- /dev/null +++ b/app/models/hyrax/flexible_schema.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true +class Hyrax::FlexibleSchema < ApplicationRecord + serialize :profile, coder: YAML + + def title + "#{profile['profile']['responsibility_statement']} - version #{id}" + end + + def attributes_for(class_name) + class_names[class_name] + end + + private + + def class_names + return @class_names if @class_names + @class_names = {} + profile['classes'].keys.each do |class_name| + @class_names[class_name] = {} + end + profile['properties'].each do |key, values| + values['available_on']['class'].each do |property_class| + # map some m3 items to what Hyrax expects + values = values_map(values) + @class_names[property_class][key] = values + end + end + @class_names + end + + def values_map(values) + values['type'] = lookup_type(values['range']) + values['form']&.transform_keys!('multi_value' => 'multiple') + values['predicate'] = values['property_uri'] + values['index_keys'] = values['indexing'] + values['multiple'] = values['multi_value'] + values + end + + def lookup_type(range) + case range + when "http://www.w3.org/2001/XMLSchema#dateTime" + 'date_time' + else + range.split('#').last.underscore + end + end +end diff --git a/app/models/hyrax/pcdm_collection.rb b/app/models/hyrax/pcdm_collection.rb index efc479bee6..b1a8ceb895 100644 --- a/app/models/hyrax/pcdm_collection.rb +++ b/app/models/hyrax/pcdm_collection.rb @@ -40,7 +40,7 @@ module Hyrax # @see Hyrax::CustomQueries::Navigators::CollectionMembers#find_members_of # class PcdmCollection < Hyrax::Resource - include Hyrax::Schema(:core_metadata) + include Hyrax::Schema(:core_metadata) unless Hyrax.config.flexible? attribute :collection_type_gid, Valkyrie::Types::String attribute :member_ids, Valkyrie::Types::Array.of(Valkyrie::Types::ID).meta(ordered: true) diff --git a/app/models/hyrax/resource.rb b/app/models/hyrax/resource.rb index a0499e96f2..e07f566f7b 100644 --- a/app/models/hyrax/resource.rb +++ b/app/models/hyrax/resource.rb @@ -31,6 +31,7 @@ module Hyrax # implementations). # class Resource < Valkyrie::Resource + include Hyrax::Flexibility if Hyrax.config.flexible? include Hyrax::Naming include Hyrax::WithEvents diff --git a/app/models/hyrax/work.rb b/app/models/hyrax/work.rb index 6ad04b35a0..a148009fad 100644 --- a/app/models/hyrax/work.rb +++ b/app/models/hyrax/work.rb @@ -94,7 +94,7 @@ module Hyrax # @see https://wiki.lyrasis.org/display/samvera/Hydra::Works+Shared+Modeling # for a historical perspective. class Work < Hyrax::Resource - include Hyrax::Schema(:core_metadata) + include Hyrax::Schema(:core_metadata) unless Hyrax.config.flexible? attribute :admin_set_id, Valkyrie::Types::ID attribute :member_ids, Valkyrie::Types::Array.of(Valkyrie::Types::ID).meta(ordered: true) diff --git a/app/services/hyrax/m3_schema_loader.rb b/app/services/hyrax/m3_schema_loader.rb new file mode 100644 index 0000000000..4b1f614ae1 --- /dev/null +++ b/app/services/hyrax/m3_schema_loader.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Hyrax + ## + # @api private + # + # Read m3 profiles from the database + # + # @see config/metadata_profiles/m3_profile.yaml for an example configuration + class M3SchemaLoader < Hyrax::SchemaLoader + private + + ## + # @param [#to_s] schema_name + # @return [Enumerable] a map from attribute names to + # types + def attributes_for(schema:, version: 1) + definitions(schema, version).each_with_object({}) do |definition, hash| + hash[definition.name] = definition.type.meta(definition.config) + end + end + + ## + # @param [Symbol] schema + # + # @return [Hash{Symbol => Hash{Symbol => Object}}] + def form_definitions_for(schema:, version: 1) + definitions(schema, version).each_with_object({}) do |definition, hash| + next if definition.form_options.empty? + + hash[definition.name] = definition.form_options + end + end + + ## + # @param [Symbol] schema + # + # @return [{Symbol => Symbol}] a map from index keys to attribute names + def index_rules_for(schema:, version: 1) + definitions(schema, version).each_with_object({}) do |definition, hash| + definition.index_keys.each do |key| + hash[key] = definition.name + end + end + end + + ## + # @api private + class AttributeDefinition + ## + # @!attr_reader :config + # @return [Hash] + # @!attr_reader :name + # @return [#to_sym] + attr_reader :config, :name + + ## + # @param [#to_sym] name + # @param [Hash] config + def initialize(name, config) + @config = config + @name = name.to_sym + end + + ## + # @return [Hash{Symbol => Object}] + def form_options + config.fetch('form', {})&.symbolize_keys || {} + end + + ## + # @return [Enumerable] + def index_keys + config.fetch('index_keys', [])&.map(&:to_sym) || [] + end + + ## + # @return [Dry::Types::Type] + def type + collection_type = if config['multiple'] + Valkyrie::Types::Array.constructor { |v| Array(v).select(&:present?) } + else + Identity + end + collection_type.of(type_for(config['type'])) + end + + ## + # @api private + # + # This class acts as a Valkyrie/Dry::Types collection with typed members, + # but instead of wrapping the given type with itself as the collection type + # (as in `Valkyrie::Types::Array.of(MyType)`), it returns the given type. + # + # @example + # Identity.of(Valkyrie::Types::String) # => Valkyrie::Types::String + # + class Identity + ## + # @param [Dry::Types::Type] + # @return [Dry::Types::Type] the type passed in + def self.of(type) + type + end + end + + private + + ## + # Maps a configuration string value to a `Valkyrie::Type`. + # + # @param [String] + # @return [Dry::Types::Type] + def type_for(type) + case type + when 'id' + Valkyrie::Types::ID + when 'uri' + Valkyrie::Types::URI + when 'date_time' + Valkyrie::Types::DateTime + else + begin + "Valkyrie::Types::#{type.capitalize}".constantize + rescue NameError + raise ArgumentError, "Unrecognized type: #{type}" + end + end + end + end + + class UndefinedSchemaError < ArgumentError; end + + private + + def definitions(_schema_name, _version) + raise NotImplementedError, 'Implement #definitions in a child class' + end + end +end diff --git a/app/services/hyrax/simple_schema_loader.rb b/app/services/hyrax/simple_schema_loader.rb index 7fb373e607..1597ee96ce 100644 --- a/app/services/hyrax/simple_schema_loader.rb +++ b/app/services/hyrax/simple_schema_loader.rb @@ -7,137 +7,19 @@ module Hyrax # This is a simple yaml config-driven schema loader # # @see config/metadata/basic_metadata.yaml for an example configuration - class SimpleSchemaLoader - ## - # @param [Symbol] schema - # - # @return [Hash] a map from attribute names to - # types - def attributes_for(schema:) - definitions(schema).each_with_object({}) do |definition, hash| - hash[definition.name] = definition.type.meta(definition.config) - end - end - - ## - # @param [Symbol] schema - # - # @return [Hash{Symbol => Hash{Symbol => Object}}] - def form_definitions_for(schema:) - definitions(schema).each_with_object({}) do |definition, hash| - next if definition.form_options.empty? - - hash[definition.name] = definition.form_options - end - end - - ## - # @param [Symbol] schema - # - # @return [{Symbol => Symbol}] a map from index keys to attribute names - def index_rules_for(schema:) - definitions(schema).each_with_object({}) do |definition, hash| - definition.index_keys.each do |key| - hash[key] = definition.name - end - end - end - + class SimpleSchemaLoader < Hyrax::SchemaLoader def permissive_schema_for_valkrie_adapter metadata_files.each_with_object({}) do |schema_name, ret_hsh| predicate_pairs(ret_hsh, schema_name) end end - ## - # @api private - class AttributeDefinition - ## - # @!attr_reader :config - # @return [Hash] - # @!attr_reader :name - # @return [#to_sym] - attr_reader :config, :name - - ## - # @param [#to_sym] name - # @param [Hash] config - def initialize(name, config) - @config = config - @name = name.to_sym - end - - ## - # @return [Hash{Symbol => Object}] - def form_options - config.fetch('form', {}).symbolize_keys - end - - ## - # @return [Enumerable] - def index_keys - config.fetch('index_keys', []).map(&:to_sym) - end - - ## - # @return [Dry::Types::Type] - def type - collection_type = if config['multiple'] - Valkyrie::Types::Array.constructor { |v| Array(v).select(&:present?) } - else - Identity - end - collection_type.of(type_for(config['type'])) - end - - ## - # @api private - # - # This class acts as a Valkyrie/Dry::Types collection with typed members, - # but instead of wrapping the given type with itself as the collection type - # (as in `Valkyrie::Types::Array.of(MyType)`), it returns the given type. - # - # @example - # Identity.of(Valkyrie::Types::String) # => Valkyrie::Types::String - # - class Identity - ## - # @param [Dry::Types::Type] - # @return [Dry::Types::Type] the type passed in - def self.of(type) - type - end - end - - private - - ## - # Maps a configuration string value to a `Valkyrie::Type`. - # - # @param [String] - # @return [Dry::Types::Type] - def type_for(type) - case type - when 'id' - Valkyrie::Types::ID - when 'uri' - Valkyrie::Types::URI - when 'date_time' - Valkyrie::Types::DateTime - else - "Valkyrie::Types::#{type.capitalize}".constantize - end - end - end - - class UndefinedSchemaError < ArgumentError; end - private ## # @param [#to_s] schema_name # @return [Enumerable` class <%= class_name %>Indexer < Hyrax::Indexers::PcdmCollectionIndexer - include Hyrax::Indexer(:<%= file_name %>) + include Hyrax::Indexer(:<%= file_name %>) unless Hyrax.config.flexible? + include Hyrax::Indexer('<%= class_name %>') if Hyrax.config.flexible? end diff --git a/lib/generators/hyrax/templates/db/migrate/20240606205215_create_hyrax_flexible_schemas.rb.erb b/lib/generators/hyrax/templates/db/migrate/20240606205215_create_hyrax_flexible_schemas.rb.erb new file mode 100644 index 0000000000..2f78c686d3 --- /dev/null +++ b/lib/generators/hyrax/templates/db/migrate/20240606205215_create_hyrax_flexible_schemas.rb.erb @@ -0,0 +1,9 @@ +class CreateHyraxFlexibleSchemas < ActiveRecord::Migration<%= migration_version %> + def change + create_table :hyrax_flexible_schemas do |t| + t.text :profile + + t.timestamps + end + end +end diff --git a/lib/generators/hyrax/work_resource/templates/indexer.rb.erb b/lib/generators/hyrax/work_resource/templates/indexer.rb.erb index 3ba45902f7..626cdac9d6 100644 --- a/lib/generators/hyrax/work_resource/templates/indexer.rb.erb +++ b/lib/generators/hyrax/work_resource/templates/indexer.rb.erb @@ -3,8 +3,9 @@ # Generated via # `rails generate hyrax:work_resource <%= class_name %>` class <%= class_name %>Indexer < Hyrax::Indexers::PcdmObjectIndexer(<%= class_name %>) - include Hyrax::Indexer(:basic_metadata) - include Hyrax::Indexer(:<%= file_name %>) + include Hyrax::Indexer(:basic_metadata) unless Hyrax.config.flexible? + include Hyrax::Indexer(:<%= file_name %>) unless Hyrax.config.flexible? + include Hyrax::Indexer('<%= class_name %>') if Hyrax.config.flexible? # Uncomment this block if you want to add custom indexing behavior: # def to_solr diff --git a/lib/hyrax/configuration.rb b/lib/hyrax/configuration.rb index c3f25099a9..24c885f96e 100644 --- a/lib/hyrax/configuration.rb +++ b/lib/hyrax/configuration.rb @@ -305,6 +305,14 @@ def fixity_service @fixity_service ||= Hyrax::Fixity::ActiveFedoraFixityService end + # This value determines whether to use m3 flexible metadata schema or not + attr_writer :flexible + attr_reader :flexible + def flexible? + @flexible ||= + ActiveModel::Type::Boolean.new.cast(ENV.fetch('HYRAX_FLEXIBLE', false)) + end + # This value determines whether to use load the Freyja adapter in dassie attr_writer :valkyrie_transition attr_reader :valkyrie_transition diff --git a/lib/hyrax/indexer.rb b/lib/hyrax/indexer.rb index f47630e9e4..7851bd39bd 100644 --- a/lib/hyrax/indexer.rb +++ b/lib/hyrax/indexer.rb @@ -33,8 +33,15 @@ class Indexer < Module def initialize(rules) define_method :to_solr do |*args| super(*args).tap do |document| - rules.each do |index_key, method| - document[index_key] = resource.try(method) + if Hyrax.config.flexible? + Hyrax::Schema.default_schema_loader.index_rules_for(schema: resource.class.to_s, version: resource.schema_version).each do |index_key, method| + document[index_key] = resource.try(method) + end + document['schema_version_ssi'] = resource.schema_version + else + rules.each do |index_key, method| + document[index_key] = resource.try(method) + end end end end diff --git a/lib/hyrax/schema.rb b/lib/hyrax/schema.rb index 0a96b2c5ad..7d545a9700 100644 --- a/lib/hyrax/schema.rb +++ b/lib/hyrax/schema.rb @@ -56,23 +56,29 @@ class Schema < Module ## # @!attribute [r] name # @return [Symbol] - attr_reader :name + attr_reader :name, :version + ## + # Pick the default schema loader based on whether flex is on or not + def self.default_schema_loader + Hyrax.config.flexible? ? M3SchemaLoader.new : SimpleSchemaLoader.new + end ## # @param [Symbol] schema_name # # @note use Hyrax::Schema(:my_schema) instead # # @api private - def initialize(schema_name, schema_loader: SimpleSchemaLoader.new) - @name = schema_name + def initialize(schema_name, schema_loader: Hyrax::Schema.default_schema_loader, schema_version: '1') + @name = schema_name.to_s + @version = schema_version @schema_loader = schema_loader end ## # @return [Hash{Symbol => Dry::Types::Type}] def attributes - @schema_loader.attributes_for(schema: name) + @schema_loader.attributes_for(schema: name, version:) end ## diff --git a/spec/fixtures/files/m3_profile.yaml b/spec/fixtures/files/m3_profile.yaml new file mode 100644 index 0000000000..e8d1dcb582 --- /dev/null +++ b/spec/fixtures/files/m3_profile.yaml @@ -0,0 +1,162 @@ +--- +m3_version: 1.0.beta2 +profile: + date_modified: '2024-06-01' + responsibility: https://samvera.org + responsibility_statement: Hyrax Initial Profile + type: + version: 1 +classes: + GenericWorkResource: + display_label: Generic Work + Monograph: + display_label: Monograph + AdminSet: + diplay_label: AdminSet + AdminSetResource: + diplay_label: AdministrativeSet + Collection: + display_label: Collection + CollectionResource: + display_label: PcdmCollection + Hyrax::FileSet: + display_label: FileSet +contexts: + flexible_context: + display_label: Flexible Metadata Example +mappings: + blacklight: + name: Additional Blacklight Solr Mappings + metatags: + name: Metatags + mods_oai_pmh: + name: MODS OAI PMH + qualified_dc_pmh: + name: Qualified DC OAI PMH + simple_dc_pmh: + name: Simple DC OAI PMH +properties: + title: + available_on: + class: + - AdminSetResource + - AdminSet + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Monograph + cardinality: + minimum: 0 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + definition: + default: Enter a standardized title for display. If only one + title is needed, transcribe the title from the source + itself. + display_label: + default: Title + index_documentation: displayable, searchable + indexing: + - 'title_sim' + - 'title_tesim' + form: + required: true + primary: true + multi_value: true + mappings: + metatags: twitter:title, og:title + mods_oai_pmh: mods:titleInfo/mods:title + qualified_dc_pmh: dcterms:title + simple_dc_pmh: dc:title + property_uri: http://purl.org/dc/terms/title + range: http://www.w3.org/2001/XMLSchema#string + requirement: required + sample_values: + - Pencil drawn portrait study of woman + date_modified: + available_on: + class: + - AdminSetResource + - AdminSet + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + display_label: + default: Date Modified + property_uri: http://purl.org/dc/terms/modified + range: http://www.w3.org/2001/XMLSchema#dateTime + sample_values: + - "2024-06-06 21:06:51 +0000" + date_uploaded: + available_on: + class: + - AdminSetResource + - AdminSet + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + display_label: + default: Date Uploaded + property_uri: http://purl.org/dc/terms/dateSubmitted + range: http://www.w3.org/2001/XMLSchema#dateTime + sample_values: + - "2024-06-06 21:06:51 +0000" + depositor: + available_on: + class: + - AdminSetResource + - AdminSet + - Hyrax::FileSet + - CollectionResource + - GenericWorkResource + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Depositor + index_documentation: searchable + indexing: + - 'depositor_tesim' + property_uri: http://id.loc.gov/vocabulary/relators/dpt + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Julie Allinson + creator: + available_on: + class: + - Hyrax::FileSet + cardinality: + minimum: 1 + multi_value: true + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Creator + index_documentation: searchable + indexing: + - 'creator_tesim' + property_uri: http://purl.org/dc/elements/1.1/creator + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Julie Allinson diff --git a/spec/hyrax/schema_spec.rb b/spec/hyrax/schema_spec.rb index 855cbbd0d8..ca1baf3977 100644 --- a/spec/hyrax/schema_spec.rb +++ b/spec/hyrax/schema_spec.rb @@ -12,7 +12,7 @@ class Resource < Hyrax::Resource; end Hyrax::Test::Schema::Resource end - after { Hyrax::Test.send(:remove_const, :Schema) } + after { Hyrax::Test.send(:remove_const, :Schema) if defined?(Hyrax::Test::Schema) } describe 'including' do it 'applies the specified schema' do @@ -96,4 +96,22 @@ class Resource < Hyrax::Resource; end expect(saved[:date_created].map { |t| DateTime.parse(t.to_s).strftime("%FT%R") }).to match_array(times_parsed) end end + + describe '.default_schema_loader' do + context 'when using flexible metadata' do + it 'returns an M3SchemaLoader' do + allow(Hyrax.config).to receive(:flexible?).and_return(true) + + expect(described_class.default_schema_loader).to be_a(Hyrax::M3SchemaLoader) + end + end + + context 'when not using flexible metadata' do + it 'returns a SimpleSchemaLoader' do + allow(Hyrax.config).to receive(:flexible?).and_return(false) + + expect(described_class.default_schema_loader).to be_a(Hyrax::SimpleSchemaLoader) + end + end + end end diff --git a/spec/models/concerns/hyrax/flexibility_spec.rb b/spec/models/concerns/hyrax/flexibility_spec.rb new file mode 100644 index 0000000000..85c2f49bfe --- /dev/null +++ b/spec/models/concerns/hyrax/flexibility_spec.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Hyrax::Flexibility' do + subject(:flexibility_class) { Hyrax::Test::Flexibility::TestWork } + let(:schema) { Hyrax::FlexibleSchema.create(profile: profile.deep_merge(test_work_profile)) } + let(:profile) { YAML.safe_load_file(Hyrax::Engine.root.join('spec', 'fixtures', 'files', 'm3_profile.yaml')) } + let(:test_work_profile) do + YAML.safe_load(<<-YAML) + classes: + Hyrax::Test::Flexibility::TestWork: + display_label: Test Work + properties: + title: + available_on: + class: + - Hyrax::Test::Flexibility::TestWork + YAML + end + + before do + allow(Hyrax.config).to receive(:flexible?).and_return(true) + allow(Hyrax::FlexibleSchema).to receive(:find).and_return(schema) + + module Hyrax::Test::Flexibility + class TestWork < Hyrax::Resource + include Hyrax::Flexibility if Hyrax.config.flexible? + end + end + end + + after do + Hyrax::Test::Flexibility.send(:remove_const, :TestWork) + allow(Hyrax.config).to receive(:flexible?).and_return(false) + end + + its(:included_modules) { is_expected.to include(Hyrax::Flexibility) } + + describe '#schema_version' do + let(:flexibility_instance) { flexibility_class.new } + + it 'responds to #schema_version' do + expect(flexibility_instance).to respond_to(:schema_version) + end + end + + describe '.attributes' do + let(:flexibility_instance) { flexibility_class.new } + + it 'defines the attribute setter methods' do + expect(flexibility_instance).to respond_to(:title=) + end + end + + describe '.new' do + context 'when attributes is a Struct' do + it 'creates a new instance with attributes converted to a hash' do + new_instance = flexibility_class.new(Struct.new(:title).new(['Test Title'])) + expect(new_instance.title).to eq(['Test Title']) + end + end + + context 'when attributes is a hash' do + it 'loads the attributes' do + instance = flexibility_class.new(title: ['Test Title']) + expect(instance.title).to eq(['Test Title']) + end + end + end +end diff --git a/spec/models/hyrax/flexible_schema_spec.rb b/spec/models/hyrax/flexible_schema_spec.rb new file mode 100644 index 0000000000..fefa70acd3 --- /dev/null +++ b/spec/models/hyrax/flexible_schema_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true +require 'rails_helper' + +RSpec.describe Hyrax::FlexibleSchema, type: :model do + let(:profile_file_path) { File.join(fixture_path, 'files', 'm3_profile.yaml') } + let(:profile_data) { YAML.load_file(profile_file_path) } + + subject { described_class.create(profile: profile_data) } + + describe '#title' do + it 'returns the correct title' do + responsibility_statement = profile_data['profile']['responsibility_statement'] + expect(subject.title).to eq("#{responsibility_statement} - version #{subject.id}") + end + end + + describe '#attributes_for' do + context 'when class_name exists' do + it 'returns the correct attributes for each class' do + profile_data['classes'].keys.each do |class_name| + attributes = subject.attributes_for(class_name) + expect(attributes).to be_a(Hash) + attributes.each do |_key, values| + expect(values).to include('type', 'predicate', 'index_keys', 'multiple') + end + end + end + end + + context 'when class_name does not exist' do + it 'returns nil' do + expect(subject.attributes_for('NonExistentClass')).to be_nil + end + end + end +end diff --git a/spec/services/hyrax/m3_schema_loader_spec.rb b/spec/services/hyrax/m3_schema_loader_spec.rb new file mode 100644 index 0000000000..1c626c5035 --- /dev/null +++ b/spec/services/hyrax/m3_schema_loader_spec.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Hyrax::M3SchemaLoader do + subject(:schema_loader) { described_class.new } + let(:profile) { YAML.safe_load_file(Hyrax::Engine.root.join('spec', 'fixtures', 'files', 'm3_profile.yaml')) } + let(:schema) do + Hyrax::FlexibleSchema.create( + profile: profile + ) + end + + before do + allow(Hyrax.config).to receive(:flexible?).and_return(true) + allow(Hyrax::FlexibleSchema).to receive(:find).and_return(schema) + end + + describe '#attributes_for' do + it 'provides an attributes hash' do + expect(schema_loader.attributes_for(schema: Monograph.to_s)) + .to include(title: Valkyrie::Types::Array.of(Valkyrie::Types::String), + depositor: Valkyrie::Types::String) + end + + it 'provides access to attribute metadata' do + expect(schema_loader.attributes_for(schema: Monograph.to_s)[:title].meta) + .to include({ "type" => "string", + "form" => { "multiple" => true, "primary" => true, "required" => true }, + "index_keys" => ["title_sim", "title_tesim"], + "multiple" => true, + "predicate" => "http://purl.org/dc/terms/title" }) + end + + context 'with generated resource' do + let(:sample_attribute) do + YAML.safe_load(<<-YAML) + properties: + sample_attribute: + available_on: + class: + - Monograph + cardinality: + minimum: 0 + maximum: 1 + multi_value: false + controlled_values: + format: http://www.w3.org/2001/XMLSchema#string + sources: + - 'null' + display_label: + default: Sample Attribute + property_uri: http://hyrax-example.com/sample_attribute + range: http://www.w3.org/2001/XMLSchema#string + sample_values: + - Example Sample Attribute + YAML + end + let(:schema) do + Hyrax::FlexibleSchema.create( + profile: profile.deep_merge(sample_attribute) + ) + end + + it 'provides an attributes hash' do + expect(schema_loader.attributes_for(schema: Monograph.to_s)) + .to include(sample_attribute: Valkyrie::Types::Array.of(Valkyrie::Types::String)) + end + end + + it 'raises an error for an undefined schema' do + expect { schema_loader.attributes_for(schema: :NOT_A_SCHEMA) } + .to raise_error described_class::UndefinedSchemaError + end + end + + describe '#index_rules_for' do + it 'provides index configuration' do + expect(schema_loader.index_rules_for(schema: Monograph.to_s)).to include(title_sim: :title, title_tesim: :title) + end + end + + describe '#form_definitions_for' do + it 'provides form configuration' do + expect(schema_loader.form_definitions_for(schema: Monograph.to_s)) + .to eq(title: { required: true, primary: true, multiple: true }) + end + end +end diff --git a/spec/services/hyrax/schema_loader_spec.rb b/spec/services/hyrax/schema_loader_spec.rb new file mode 100644 index 0000000000..139986d18b --- /dev/null +++ b/spec/services/hyrax/schema_loader_spec.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Hyrax::SchemaLoader do + subject(:schema_loader) { described_class.new } + + describe '#definitions' do + it 'raises NotImplementedError' do + expect { schema_loader.send(:definitions, :some_schema, 1) } + .to raise_error(NotImplementedError, 'Implement #definitions in a child class') + end + end +end + +RSpec.describe Hyrax::SchemaLoader::AttributeDefinition do + subject(:attribute_definition) { described_class.new(name, config) } + + let(:name) { 'title' } + let(:config) do + { 'type' => 'string', + 'form' => { 'multiple' => true, 'primary' => true, 'required' => true }, + 'index_keys' => ['title_sim', 'title_tesim'], + 'multiple' => true, + 'predicate' => 'http://purl.org/dc/terms/title' } + end + + describe '#form_options' do + it 'returns form options as a symbolized hash' do + expect(attribute_definition.form_options).to eq(multiple: true, primary: true, required: true) + end + + context 'when form options are missing' do + let(:config) { { 'type' => 'string' } } + + it 'returns an empty hash' do + expect(attribute_definition.form_options).to eq({}) + end + end + end + + describe '#index_keys' do + it 'returns index keys as symbols' do + expect(attribute_definition.index_keys).to eq([:title_sim, :title_tesim]) + end + + context 'when index keys are missing' do + let(:config) { { 'type' => 'string' } } + + it 'returns an empty array' do + expect(attribute_definition.index_keys).to eq([]) + end + end + end + + describe '#type' do + context 'when multiple is true' do + it 'returns a Valkyrie array type' do + expect(attribute_definition.type).to be_a(Dry::Types::Constructor) + expect(attribute_definition.type.to_s).to include('Array') + expect(attribute_definition.type.member).to eq(Valkyrie::Types::String) + end + end + + context 'when multiple is false' do + let(:config) { { 'type' => 'string', 'multiple' => false } } + + it 'returns a Valkyrie string type' do + expect(attribute_definition.type).to eq(Valkyrie::Types::String) + end + end + + context 'when type is id' do + let(:config) { { 'type' => 'id' } } + + it 'returns a Valkyrie ID type' do + expect(attribute_definition.type).to eq(Valkyrie::Types::ID) + end + end + + context 'when type is uri' do + let(:config) { { 'type' => 'uri' } } + + it 'returns a Valkyrie URI type' do + expect(attribute_definition.type).to eq(Valkyrie::Types::URI) + end + end + + context 'when type is date_time' do + let(:config) { { 'type' => 'date_time' } } + + it 'returns a Valkyrie DateTime type' do + expect(attribute_definition.type).to eq(Valkyrie::Types::DateTime) + end + end + + context 'when type is not recognized' do + let(:config) { { 'type' => 'custom' } } + + it 'raises an ArgumentError' do + expect { attribute_definition.type }.to raise_error(ArgumentError, 'Unrecognized type: custom') + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 0d160047e6..099441214c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -5,6 +5,7 @@ # Analytics is turned off by default ENV['HYRAX_ANALYTICS'] = 'false' +ENV['HYRAX_FLEXIBLE'] = 'false' require "bundler/setup"