From a34ba363b9078866ae06f0a046154f54494b4b9b Mon Sep 17 00:00:00 2001 From: Martin Simpson Date: Sun, 4 Feb 2024 13:26:54 +0700 Subject: [PATCH] Updated schema yaml and included further items --- TODO.md | 2 +- .../hyku_knapsack/collection_form_behavior.rb | 48 +++++ .../generic_work_form_overrides.rb | 30 ++++ .../hyku_knapsack/image_form_overrides.rb | 23 +++ .../person_or_organization_form_behavior.rb | 57 ++++++ app/forms/concerns/hyku_knapsack/work_base.rb | 95 ++++++++++ app/forms/concerns/hyku_knapsack/work_form.rb | 164 ++++++++++++++++++ .../una_open_educational_resource_form.rb | 4 +- .../hyku_knapsack/task_master/publish_job.rb | 13 ++ .../task_master/account_behavior.rb | 32 ++++ .../task_master/file_set_behavior.rb | 65 +++++++ .../hyku_knapsack/task_master/publishable.rb | 56 ++++++ .../task_master/work_behavior.rb | 36 ++++ .../concerns/hyku_knapsack/work_behavior.rb | 36 ++++ app/models/una_open_educational_resource.rb | 4 +- ...una_open_educational_resource_presenter.rb | 6 +- .../task_master/publish_service.rb | 75 ++++++++ .../una_open_educational_resource.yaml | 22 +-- hyku_knapsack.gemspec | 2 +- .../person_or_organization_form_behavior.rb | 57 ++++++ spec/rails_helper.rb | 2 +- 21 files changed, 808 insertions(+), 21 deletions(-) create mode 100644 app/forms/concerns/hyku_knapsack/collection_form_behavior.rb create mode 100644 app/forms/concerns/hyku_knapsack/generic_work_form_overrides.rb create mode 100644 app/forms/concerns/hyku_knapsack/image_form_overrides.rb create mode 100644 app/forms/concerns/hyku_knapsack/person_or_organization_form_behavior.rb create mode 100644 app/forms/concerns/hyku_knapsack/work_base.rb create mode 100644 app/forms/concerns/hyku_knapsack/work_form.rb create mode 100644 app/jobs/hyku_knapsack/task_master/publish_job.rb create mode 100644 app/models/concerns/hyku_knapsack/task_master/account_behavior.rb create mode 100644 app/models/concerns/hyku_knapsack/task_master/file_set_behavior.rb create mode 100644 app/models/concerns/hyku_knapsack/task_master/publishable.rb create mode 100644 app/models/concerns/hyku_knapsack/task_master/work_behavior.rb create mode 100644 app/models/concerns/hyku_knapsack/work_behavior.rb create mode 100644 app/services/hyku_knapsack/task_master/publish_service.rb create mode 100644 lib/hyku_knapsack/person_or_organization_form_behavior.rb diff --git a/TODO.md b/TODO.md index 052cc23..1b54d7c 100644 --- a/TODO.md +++ b/TODO.md @@ -12,7 +12,7 @@ #include Hyrax::Autopopulation::AutopopulationProperty #TODO: re-add Behavior - #include HykuAddons::TaskMaster::WorkBehavior + #include HykuKnapsack::TaskMaster::WorkBehavior ``` diff --git a/app/forms/concerns/hyku_knapsack/collection_form_behavior.rb b/app/forms/concerns/hyku_knapsack/collection_form_behavior.rb new file mode 100644 index 0000000..0cc645f --- /dev/null +++ b/app/forms/concerns/hyku_knapsack/collection_form_behavior.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module HykuKnapsack + module CollectionFormBehavior + extend ActiveSupport::Concern + + include HykuKnapsack::PersonOrOrganizationFormBehavior + + # TODO: extract duplicate code (from JSONFieldsActor) out into a service class? + # rubocop:disable Metrics/BlockLength + class_methods do + def model_attributes(form_params) + super.tap do |model_attributes| + [:creator, :contributor].each do |field| + if name_blank?(field, model_attributes[field]&.map(&:to_h)) || recursive_blank?(model_attributes[field]&.map(&:to_h)) + model_attributes.delete(field) + else + model_attributes[field] = model_attributes[field].to_json + end + model_attributes[field] = Array(model_attributes[field]) if multiple?(field) + end + end + end + + def name_blank?(field, obj) + return false unless field.in? [:creator, :contributor, :editor] + recursive_blank?(Array(obj).map { |o| o.reject { |k, _v| k == "#{field}_name_type" } }) + end + + def recursive_blank?(obj) + case obj + when Hash + obj.values.all? { |o| recursive_blank?(o) } + when Array + obj.all? { |o| recursive_blank?(o) } + else + obj.blank? + end + end + end + # rubocop:enable Metrics/BlockLength + + # User collections will error without this, as _title is used in for works and collections + def schema_driven? + false + end + end +end diff --git a/app/forms/concerns/hyku_knapsack/generic_work_form_overrides.rb b/app/forms/concerns/hyku_knapsack/generic_work_form_overrides.rb new file mode 100644 index 0000000..5c7f785 --- /dev/null +++ b/app/forms/concerns/hyku_knapsack/generic_work_form_overrides.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module HykuKnapsack + module GenericWorkFormOverrides + extend ActiveSupport::Concern + + include ::HykuKnapsack::WorkForm + + included do + # version is used in the show page but populated by version_number from the edit and new form + add_terms %i[title resource_type creator alt_title contributor rendering_ids abstract date_published media + duration institution org_unit project_name funder fndr_project_ref event_title event_location + event_date series_name book_title editor journal_title alternative_journal_title volume edition + version_number issue pagination article_num publisher place_of_publication isbn issn eissn + current_he_institution date_accepted date_submitted official_link related_url related_exhibition + related_exhibition_venue related_exhibition_date language license rights_statement rights_holder doi + qualification_name qualification_level alternate_identifier related_identifier refereed keyword dewey + library_of_congress_classification add_info] + self.required_fields = %i[title resource_type creator institution date_published] + + # These includes are after add_terms because they add terms + include Hyrax::DOI::DOIFormBehavior + include Hyrax::DOI::DataCiteDOIFormBehavior + + def primary_terms + super - %i[license] + end + end + end +end diff --git a/app/forms/concerns/hyku_knapsack/image_form_overrides.rb b/app/forms/concerns/hyku_knapsack/image_form_overrides.rb new file mode 100644 index 0000000..0006347 --- /dev/null +++ b/app/forms/concerns/hyku_knapsack/image_form_overrides.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module HykuKnapsack + module ImageFormOverrides + extend ActiveSupport::Concern + + include ::HykuKnapsack::WorkForm + + included do + add_terms %i[title resource_type creator alt_title contributor rendering_ids abstract date_published media + duration institution org_unit project_name funder fndr_project_ref + publisher place_of_publication date_accepted date_submitted official_link related_url + related_exhibition related_exhibition_venue related_exhibition_date language license rights_statement + rights_holder doi alternate_identifier related_identifier refereed keyword dewey + library_of_congress_classification add_info] + self.required_fields = %i[title resource_type creator institution date_published] + + # These must be added after the terms are defined + include Hyrax::DOI::DOIFormBehavior + include Hyrax::DOI::DataCiteDOIFormBehavior + end + end +end diff --git a/app/forms/concerns/hyku_knapsack/person_or_organization_form_behavior.rb b/app/forms/concerns/hyku_knapsack/person_or_organization_form_behavior.rb new file mode 100644 index 0000000..dd9c956 --- /dev/null +++ b/app/forms/concerns/hyku_knapsack/person_or_organization_form_behavior.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module HykuKnapsack + module PersonOrOrganizationFormBehavior + extend ActiveSupport::Concern + + # Helper methods for JSON fields + def creator_list + person_or_organization_list(:creator) + end + + def contributor_list + person_or_organization_list(:contributor) + end + + # rubocop:disable Metrics/BlockLength + class_methods do + # Group all params here so save on boiler plate + def build_permitted_params + super.tap do |permitted_params| + permitted_params << creator_fields + permitted_params << contributor_fields + end + end + + def creator_fields + { + creator: [ + :creator_organization_name, :creator_given_name, :creator_middle_name, :creator_family_name, + :creator_name_type, :creator_orcid, :creator_isni, :creator_ror, :creator_grid, :creator_wikidata, + :creator_suffix, :creator_institution, :creator_institutional_email, :creator_computing_id, + :creator_profile_visibility, creator_institution: [], creator_role: [], creator_institutional_relationship: [] + ] + } + end + + def contributor_fields + { + contributor: [ + :contributor_organization_name, :contributor_given_name, :contributor_middle_name, + :contributor_family_name, :contributor_name_type, :contributor_orcid, :contributor_isni, :contributor_ror, + :contributor_grid, :contributor_wikidata, :contributor_suffix, :contributor_type, :contributor_institution, + contributor_institution: [], contributor_role: [], contributor_institutional_relationship: [] + ] + } + end + end + + private + + def person_or_organization_list(field) + # Return empty hash to ensure that it gets rendered at least once + return [{}] if send(field)&.first.blank? + JSON.parse(send(field).first) + end + end +end diff --git a/app/forms/concerns/hyku_knapsack/work_base.rb b/app/forms/concerns/hyku_knapsack/work_base.rb new file mode 100644 index 0000000..78cc99e --- /dev/null +++ b/app/forms/concerns/hyku_knapsack/work_base.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +module HykuKnapsack + module WorkBase + extend ActiveSupport::Concern + + include Hyrax::Autopopulation::AutopopulationProperty + include HykuKnapsack::TaskMaster::WorkBehavior + + # TODO: Review indexing and switch to mostly _ssim instead of _tesim + # rubocop:disable Metrics/BlockLength + included do + # From SharedMetadata + property :official_link, predicate: ::RDF::Vocab::SCHEMA.url, multiple: false do |index| + index.as :stored_searchable + end + + # From BasicMetadataDecorator + property :institution, predicate: ::RDF::Vocab::ORG.organization do |index| + index.as :stored_searchable, :facetable + end + + property :org_unit, predicate: ::RDF::Vocab::ORG.OrganizationalUnit do |index| + index.as :stored_searchable, :facetable + end + + property :funder, predicate: ::RDF::Vocab::MARCRelators.fnd do |index| + index.as :stored_searchable + end + + # FIXME: Dates should be indexed as dates or at least _ssim not _tesim + property :date_published, predicate: ::RDF::Vocab::DC.available, multiple: false do |index| + index.as :stored_searchable + end + + property :date_accepted, predicate: ::RDF::Vocab::DC.dateAccepted, multiple: false do |index| + index.as :stored_searchable + end + + property :date_submitted, predicate: ::RDF::Vocab::Bibframe.originDate, multiple: false do |index| + index.as :stored_searchable + end + + property :project_name, predicate: ::RDF::Vocab::BF2.term(:CollectiveTitle) do |index| + index.as :stored_searchable + end + + property :rights_holder, predicate: ::RDF::Vocab::DC.rightsHolder do |index| + index.as :stored_searchable + end + + property :abstract, predicate: ::RDF::Vocab::DC.abstract, multiple: false do |index| + index.type :text + index.as :stored_searchable + end + + property :alternate_identifier, predicate: ::RDF::Vocab::BF2.term(:Local) do |index| + index.as :stored_searchable + end + + property :related_identifier, predicate: ::RDF::Vocab::BF2.identifiedBy do |index| + index.as :stored_searchable + end + + property :library_of_congress_classification, predicate: ::RDF::Vocab::BF2.term(:ClassificationLcc) do |index| + index.as :stored_searchable, :facetable + end + + property :dewey, predicate: ::RDF::Vocab::SCHEMA.CategoryCode, multiple: false do |index| + index.as :stored_searchable + end + + property :note, predicate: ::RDF::Vocab::MODS.note, multiple: true do |index| + index.as :stored_searchable + end + + # property :file_availability, predicate: ::RDF::Vocab::SCHEMA.ItemAvailability do |index| + # index.as :stored_searchable, :facetable + # end + + property :source_identifier, predicate: ::RDF::Vocab::PROV.wasDerivedFrom, multiple: false do |index| + index.as :stored_searchable + end + + class_attribute :json_fields, :date_fields + self.json_fields = %i[creator contributor funder alternate_identifier related_identifier] + self.date_fields = %i[date_published date_accepted date_submitted] + end + # rubocop:enable Metrics/BlockLength + + def schema_driven? + false + end + end +end diff --git a/app/forms/concerns/hyku_knapsack/work_form.rb b/app/forms/concerns/hyku_knapsack/work_form.rb new file mode 100644 index 0000000..3660f3a --- /dev/null +++ b/app/forms/concerns/hyku_knapsack/work_form.rb @@ -0,0 +1,164 @@ +# frozen_string_literal: true + +module HykuKnapsack + module WorkForm + extend ActiveSupport::Concern + + include HykuKnapsack::PersonOrOrganizationFormBehavior + + # rubocop:disable Metrics/BlockLength + class_methods do + # Group all params here so save on boiler plate + # rubocop:disable Metrics/MethodLength + # rubocop:disable Metrics/AbcSize + def build_permitted_params + super.tap do |permitted_params| + permitted_params << common_fields + permitted_params << file_set_fields + permitted_params << date_published_fields + permitted_params << date_accepted_fields + permitted_params << date_submitted_fields + permitted_params << editor_fields + permitted_params << funder_fields + permitted_params << alternate_identifier_fields + permitted_params << related_identifier_fields + permitted_params << alternate_identifier_fields + permitted_params << event_fields + permitted_params << current_he_institution_fields + permitted_params << related_exhibition_fields + permitted_params << note + end + end + # rubocop:enable Metrics/MethodLength + # rubocop:enable Metrics/AbcSize + + # Adds the terms received as params to the work type terms list ensuring the correct order + # @param work_type_terms [Array] array of terms to add to the work type + def add_terms(work_type_terms = []) + self.terms ||= hyrax_terms + self.terms = (Array.wrap(work_type_terms) + self.terms) & available_terms + end + + # Form fields. Note, these to not necessarily match the params which need to be permitted + def available_terms + %i[title alt_title resource_type creator contributor abstract date_published media duration + institution org_unit project_name funder fndr_project_ref event_title event_location event_date + series_name book_title editor journal_title alternative_journal_title volume edition version_number issue + pagination article_num publisher place_of_publication isbn issn eissn current_he_institution date_accepted + date_submitted official_link related_url related_exhibition related_exhibition_venue related_exhibition_date + language license rights_statement rights_holder doi qualification_name qualification_level draft_doi + alternate_identifier related_identifier refereed keyword dewey library_of_congress_classification add_info + page_display_order_number irb_number irb_status subject additional_links is_included_in buy_book challenged + location outcome participant reading_level photo_caption photo_description degree longitude latitude alt_email + alt_book_title table_of_contents prerequisites suggested_student_reviewers suggested_reviewers adapted_from + audience related_material note advisor subject_text mesh journal_frequency funding_description + citation references extent medium source committee_member time qualification_grantor date_published_text + rights_statement_text qualification_subject_text is_format_of part_of + georeferenced source_identifier mentor repository_space] + hyrax_terms + end + + def hyrax_terms + %i[visibility files visibility_during_embargo embargo_release_date visibility_after_embargo + visibility_during_lease lease_expiration_date visibility_after_lease admin_set_id member_of_collection_ids + ordered_member_ids in_works_ids rendering_ids representative_id thumbnail_id] + end + + def common_fields + %i[title alt_title resource_type abstract alternative_name project_name institution media official_link + related_url language license rights_statement rights_holder doi refereed keywords dewey + library_of_congress_classification add_info issn isbn eissn version_number series_name book_title pagination + publisher place_of_publication journal_title alternative_journal_title volume edition issue article_num + qualification_name qualification_level representative_id thumbnail_id] + end + + def file_set_fields + { file_set: [:visibility, :visibility_during_embargo, :embargo_release_date, :visibility_after_embargo, + :visibility_during_lease, :lease_expiration_date, :visibility_after_lease, :uploaded_file_id] } + end + + def date_accepted_fields + { date_accepted: [:date_accepted_year, :date_accepted_month, :date_accepted_day] } + end + + def date_published_fields + { date_published: [:date_published_year, :date_published_month, :date_published_day] } + end + + def date_submitted_fields + { date_submitted: [:date_submitted_year, :date_submitted_month, :date_submitted_day] } + end + + def editor_fields + { + editor: [:editor_isni, :editor_orcid, :editor_family_name, :editor_given_name, :editor_organisational_name, + :editor_institutional_relationship] + } + end + + def funder_fields + { funder: [:funder_name, :funder_doi, :funder_isni, :funder_ror, funder_award: []] } + end + + def alternate_identifier_fields + { alternate_identifier: [:alternate_identifier, :alternate_identifier_type] } + end + + def related_identifier_fields + { related_identifier: [:related_identifier, :related_identifier_type, :relation_type] } + end + + def event_fields + [:event_title, :event_location, event_date: [:event_date_year, :event_date_month, :event_date_day]] + end + + def current_he_institution_fields + { + current_he_institution: [:current_he_institution_name, :current_he_institution_isni, + :current_he_institution_ror] + } + end + + def related_exhibition_fields + [ + :related_exhibition, + :related_exhibition_venue, + related_exhibition_date: [:related_exhibition_date_year, :related_exhibition_date_month, + :related_exhibition_date_day] + ] + end + + def note + [:note] + end + end + # rubocop:enable Metrics/BlockLength + + def primary_terms + if Flipflop.enabled?(:simplified_admin_set_selection) + super + %i[admin_set_id doi] + else + super + %i[doi] + end + end + + def initialize(model, current_ability, controller) + model.admin_set_id = controller.params["admin_set_id"] if simplfied_admin_set?(controller) + + super(model, current_ability, controller) + end + + def editor_list + person_or_organization_list(:editor) + end + + def schema_driven? + false + end + + protected + + def simplfied_admin_set?(controller) + Flipflop.enabled?(:simplified_admin_set_selection) && controller&.params&.dig("admin_set_id").present? + end + end +end \ No newline at end of file diff --git a/app/forms/hyrax/una_open_educational_resource_form.rb b/app/forms/hyrax/una_open_educational_resource_form.rb index 1f8c70d..f91d39b 100644 --- a/app/forms/hyrax/una_open_educational_resource_form.rb +++ b/app/forms/hyrax/una_open_educational_resource_form.rb @@ -3,8 +3,8 @@ module Hyrax class UnaOpenEducationalResourceForm < Hyrax::Forms::WorkForm include Hyrax::DOI::DataCiteDOIFormBehavior - include Hyku::Schema::WorkForm - include Hyku::Schema::Hyrax::FormFields(:una_open_educational_resource) + include HykuKnapsack::WorkForm + include Hyku::Plugin::Schema::Yaml::Hyrax::FormFields(:una_open_educational_resource) self.model_class = ::UnaOpenEducationalResource end diff --git a/app/jobs/hyku_knapsack/task_master/publish_job.rb b/app/jobs/hyku_knapsack/task_master/publish_job.rb new file mode 100644 index 0000000..e90e755 --- /dev/null +++ b/app/jobs/hyku_knapsack/task_master/publish_job.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module HykuKnapsack + module TaskMaster + class PublishJob < ApplicationJob + discard_on ArgumentError + + def perform(type, action, json) + HykuKnapsack::TaskMaster::PublishService.new(type, action, json).perform + end + end + end +end diff --git a/app/models/concerns/hyku_knapsack/task_master/account_behavior.rb b/app/models/concerns/hyku_knapsack/task_master/account_behavior.rb new file mode 100644 index 0000000..406ab2c --- /dev/null +++ b/app/models/concerns/hyku_knapsack/task_master/account_behavior.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module HykuKnapsack + module TaskMaster + module AccountBehavior + extend ActiveSupport::Concern + + include HykuKnapsack::TaskMaster::Publishable + + def upsertable? + task_master_uuid.present? + end + + def task_master_type + "tenant" + end + + def task_master_uuid + tenant + end + + def to_task_master + { + uuid: task_master_uuid, + cname: cname, + gcloud_zone: ENV["GCLOUD_ZONE"], + google_project_id: ENV["GCLOUD_PROJECT_ID"] + } + end + end + end +end diff --git a/app/models/concerns/hyku_knapsack/task_master/file_set_behavior.rb b/app/models/concerns/hyku_knapsack/task_master/file_set_behavior.rb new file mode 100644 index 0000000..f4616f5 --- /dev/null +++ b/app/models/concerns/hyku_knapsack/task_master/file_set_behavior.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +module HykuKnapsack + module TaskMaster + module FileSetBehavior + extend ActiveSupport::Concern + + include HykuKnapsack::TaskMaster::Publishable + + included do + after_update_index :task_master_publish_file_size + end + + def task_master_type + "file" + end + + def task_master_uuid + id + end + + def to_task_master + { + uuid: task_master_uuid, + work: work_id, + name: label, + metadata: attributes.merge(file_set_charcterizations) + } + end + + # File entries will cause an error unless they have a valid work id + def upsertable? + work_id.present? + end + + protected + + # FileSets will not have their metadata until after this has already ran 5 times (after 5 updates inside of + # the actor stack). It's better to use the FileSet after_update_index hook which runs after metadata is added + def publish_upsert; end + + def task_master_publish_file_size + return unless analysed? + + publish(task_master_type, "upsert", to_task_master) + end + + def file_set_charcterizations + characterization_terms.map do |term| + next unless characterization_proxy.respond_to?(term) + + [term, characterization_proxy.send(term)] + end.compact.to_h + end + + def work_id + member_of_works&.first&.id + end + + def analysed? + original_file.present? && original_file.file_size.present? + end + end + end +end diff --git a/app/models/concerns/hyku_knapsack/task_master/publishable.rb b/app/models/concerns/hyku_knapsack/task_master/publishable.rb new file mode 100644 index 0000000..5ac6818 --- /dev/null +++ b/app/models/concerns/hyku_knapsack/task_master/publishable.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module HykuKnapsack + module TaskMaster + module Publishable + extend ActiveSupport::Concern + + included do + after_save :publish_upsert + after_destroy :publish_destroy + end + + def to_task_master + raise NotImplementedError + end + + def task_master_uuid + raise NotImplementedError + end + + def task_master_type + raise NotImplementedError + end + + def upsertable? + true + end + + def destroyable? + true + end + + protected + + def publish_upsert + publish(task_master_type, "upsert", to_task_master) + end + + def publish_destroy + publish(task_master_type, "destroy", uuid: task_master_uuid) + end + + def publish(type, action, data) + return unless enabled? && send("#{action}able?") + + HykuKnapsack::TaskMaster::PublishJob.perform_later(type, action, data.to_json) + end + + private + + def enabled? + Flipflop.enabled?(:task_master) + end + end + end +end diff --git a/app/models/concerns/hyku_knapsack/task_master/work_behavior.rb b/app/models/concerns/hyku_knapsack/task_master/work_behavior.rb new file mode 100644 index 0000000..d5a5fe4 --- /dev/null +++ b/app/models/concerns/hyku_knapsack/task_master/work_behavior.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module HykuKnapsack + module TaskMaster + module WorkBehavior + extend ActiveSupport::Concern + + include HykuKnapsack::TaskMaster::Publishable + + def upsertable? + task_master_uuid.present? + end + + def task_master_type + "work" + end + + def task_master_uuid + id + end + + def to_task_master + { + tenant: Site.instance&.account&.tenant, + uuid: task_master_uuid, + metadata: attributes + } + end + + protected + + # Upsert for works is performed inside the actor stack + def publish_upsert; end + end + end +end diff --git a/app/models/concerns/hyku_knapsack/work_behavior.rb b/app/models/concerns/hyku_knapsack/work_behavior.rb new file mode 100644 index 0000000..d5a5fe4 --- /dev/null +++ b/app/models/concerns/hyku_knapsack/work_behavior.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module HykuKnapsack + module TaskMaster + module WorkBehavior + extend ActiveSupport::Concern + + include HykuKnapsack::TaskMaster::Publishable + + def upsertable? + task_master_uuid.present? + end + + def task_master_type + "work" + end + + def task_master_uuid + id + end + + def to_task_master + { + tenant: Site.instance&.account&.tenant, + uuid: task_master_uuid, + metadata: attributes + } + end + + protected + + # Upsert for works is performed inside the actor stack + def publish_upsert; end + end + end +end diff --git a/app/models/una_open_educational_resource.rb b/app/models/una_open_educational_resource.rb index bc9e13f..4a4b9b2 100644 --- a/app/models/una_open_educational_resource.rb +++ b/app/models/una_open_educational_resource.rb @@ -3,8 +3,8 @@ class UnaOpenEducationalResource < ActiveFedora::Base include Hyrax::WorkBehavior include Hyrax::DOI::DataCiteDOIBehavior - include Hyku::Schema::WorkBase - include Hyku::Schema::Hyrax::Schema(:una_open_educational_resource) + include Hyku::Plugin::Schema::Yaml::WorkBase + include Hyku::Plugin::Schema::Yaml::Hyrax::Schema(:una_open_educational_resource) # Included after other field definitions include Hyrax::BasicMetadata diff --git a/app/presenters/hyrax/una_open_educational_resource_presenter.rb b/app/presenters/hyrax/una_open_educational_resource_presenter.rb index b20d02c..6606b29 100644 --- a/app/presenters/hyrax/una_open_educational_resource_presenter.rb +++ b/app/presenters/hyrax/una_open_educational_resource_presenter.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true module Hyrax class UnaOpenEducationalResourcePresenter < Hyrax::WorkShowPresenter - include ::HykuAddons::Schema::Presenter(:una_open_educational_resource) - include ::HykuAddons::WorkPresenterBehavior - include ::HykuAddons::PresenterDelegatable + include ::Hyku::Pluin::Schema::Yaml::Presenter(:una_open_educational_resource) + include ::HykuKnapsack::WorkPresenterBehavior + include ::HykuKnapsack::PresenterDelegatable end end diff --git a/app/services/hyku_knapsack/task_master/publish_service.rb b/app/services/hyku_knapsack/task_master/publish_service.rb new file mode 100644 index 0000000..a711dfd --- /dev/null +++ b/app/services/hyku_knapsack/task_master/publish_service.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require "google/cloud/pubsub" + +module HykuKnapsack + module TaskMaster + class PublishService + ALLOWED_TYPES = %w[tenant file work].freeze + ALLOWED_ACTIONS = %w[upsert destroy].freeze + + ## + # @api public + # + # Init + + # @param [String] the type instance type + # @param [String] the CRUD action being performed + # @param [JSON] the JSON string to be published + # + # @return [HykuKnapsack::TaskMaster::PublishService] + def initialize(type, action, json) + @type = type + @action = action + @json = json + + validate_arguments! + end + + ## + # @api public + # + # Publish a message to Google PubSub + # + # @return [Google::Cloud::PubSub::Message] or nil + def perform + # Without this check, a lot of specs will fail + return unless Flipflop.enabled?(:task_master) + + topic = client.topic(topic_name) + topic.publish(@json) + end + + protected + + def topic_name + "repository--#{@type}-#{@action}" + end + + def client + @client ||= begin + Google::Cloud::PubSub.configure do |config| + config.project_id = pubsub_credentials.dig("project_id") + config.credentials = pubsub_credentials + end + + Google::Cloud::PubSub.new + end + end + + def pubsub_credentials + raise KeyError, "Service environment variable is not set" if ENV["PUBSUB_SERVICEACCOUNT_KEY"].blank? + + @pubsub_credentials ||= JSON.parse(ENV["PUBSUB_SERVICEACCOUNT_KEY"]) + end + + private + + def validate_arguments! + raise ArgumentError, "Type '#{@type}' is invalid" unless ALLOWED_TYPES.include?(@type.to_s) + raise ArgumentError, "Action '#{@action}' is invalid" unless ALLOWED_ACTIONS.include?(@action.to_s) + raise ArgumentError, "A JSON string is required" unless @json.is_a?(String) + end + end + end +end diff --git a/config/metadata/una_open_educational_resource.yaml b/config/metadata/una_open_educational_resource.yaml index a6129a5..1bbecdf 100644 --- a/config/metadata/una_open_educational_resource.yaml +++ b/config/metadata/una_open_educational_resource.yaml @@ -34,7 +34,7 @@ attributes: primary: true multiple: true type: select - authority: HykuAddons::ResourceTypesService + authority: HykuKnapsack::ResourceTypesService include_blank: true input: single_multi_value_select creator: @@ -54,7 +54,7 @@ attributes: required: true multiple: false type: select - authority: HykuAddons::NameTypeService + authority: HykuKnapsack::NameTypeService creator_family_name: type: string form: @@ -101,7 +101,7 @@ attributes: required: false multiple: true type: select - authority: HykuAddons::RoleService + authority: HykuKnapsack::RoleService include_blank: true display_for: - Personal @@ -127,7 +127,7 @@ attributes: required: false multiple: false type: select - authority: HykuAddons::InstitutionalRelationshipService + authority: HykuKnapsack::InstitutionalRelationshipService attributes: multiple: multiple display_for: @@ -215,7 +215,7 @@ attributes: primary: true multiple: true type: select - authority: HykuAddons::SubjectService + authority: HykuKnapsack::SubjectService org_unit: type: string predicate: http://www.w3.org/ns/org#OrganizationalUnit @@ -239,7 +239,7 @@ attributes: primary: true multiple: true type: select - authority: HykuAddons::AudienceService + authority: HykuKnapsack::AudienceService language: predicate: http://purl.org/dc/elements/1.1/language multiple: true @@ -250,7 +250,7 @@ attributes: primary: true multiple: true type: select - authority: HykuAddons::LanguageService + authority: HykuKnapsack::LanguageService license: predicate: http://purl.org/dc/terms/rights multiple: true @@ -261,7 +261,7 @@ attributes: primary: true multiple: true type: select - authority: HykuAddons::LicenseService + authority: HykuKnapsack::LicenseService table_of_contents: type: string predicate: http://bibframe.org/vocab/tableOfContents @@ -378,7 +378,7 @@ attributes: required: false multiple: false type: select - authority: HykuAddons::NameTypeService + authority: HykuKnapsack::NameTypeService contributor_family_name: type: string form: @@ -409,7 +409,7 @@ attributes: required: false multiple: false type: select - authority: HykuAddons::ContributorInstitutionalRelationshipService + authority: HykuKnapsack::ContributorInstitutionalRelationshipService attributes: multiple: multiple display_for: @@ -461,7 +461,7 @@ attributes: required: false multiple: true type: select - authority: HykuAddons::RoleService + authority: HykuKnapsack::RoleService display_for: - Personal - Organizational diff --git a/hyku_knapsack.gemspec b/hyku_knapsack.gemspec index 53c9929..9bfff67 100644 --- a/hyku_knapsack.gemspec +++ b/hyku_knapsack.gemspec @@ -25,7 +25,7 @@ Gem::Specification.new do |spec| spec.add_dependency "rails", ">= 5.2.0" spec.add_dependency "hyku_plugin_google_cloud_url_mask" - spec.add_dependency "hyku-schema-yaml" + spec.add_dependency "hyku-plugin-schema-yaml" spec.add_dependency "blacklight_advanced_search" diff --git a/lib/hyku_knapsack/person_or_organization_form_behavior.rb b/lib/hyku_knapsack/person_or_organization_form_behavior.rb new file mode 100644 index 0000000..e81030d --- /dev/null +++ b/lib/hyku_knapsack/person_or_organization_form_behavior.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module HykuKnapsack + module PersonOrOrganizationFormBehavior + extend ActiveSupport::Concern + + # Helper methods for JSON fields + def creator_list + person_or_organization_list(:creator) + end + + def contributor_list + person_or_organization_list(:contributor) + end + + # rubocop:disable Metrics/BlockLength + class_methods do + # Group all params here so save on boiler plate + def build_permitted_params + super.tap do |permitted_params| + permitted_params << creator_fields + permitted_params << contributor_fields + end + end + + def creator_fields + { + creator: [ + :creator_organization_name, :creator_given_name, :creator_middle_name, :creator_family_name, + :creator_name_type, :creator_orcid, :creator_isni, :creator_ror, :creator_grid, :creator_wikidata, + :creator_suffix, :creator_institution, :creator_institutional_email, :creator_computing_id, + :creator_profile_visibility, creator_institution: [], creator_role: [], creator_institutional_relationship: [] + ] + } + end + + def contributor_fields + { + contributor: [ + :contributor_organization_name, :contributor_given_name, :contributor_middle_name, + :contributor_family_name, :contributor_name_type, :contributor_orcid, :contributor_isni, :contributor_ror, + :contributor_grid, :contributor_wikidata, :contributor_suffix, :contributor_type, :contributor_institution, + contributor_institution: [], contributor_role: [], contributor_institutional_relationship: [] + ] + } + end + end + + private + + def person_or_organization_list(field) + # Return empty hash to ensure that it gets rendered at least once + return [{}] if send(field)&.first.blank? + JSON.parse(send(field).first) + end + end +end \ No newline at end of file diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 7c92fd4..32f9c03 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -11,7 +11,7 @@ require "rspec/rails" # Add additional requires below this line. Rails is not loaded until this point! require "factory_bot_rails" -FactoryBot.definition_file_paths = [File.expand_path("spec/factories", HykuAddons::Engine.root)] +FactoryBot.definition_file_paths = [File.expand_path("spec/factories", HykuKnapsack::Engine.root)] FactoryBot.find_definitions # Requires supporting ruby files with custom matchers and macros, etc, in