diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index eb0b1c09b..0e050a065 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -2,7 +2,6 @@ class ActivitiesController < ApplicationController include Countable before_action :set_activity, only: [:show] - before_action :set_include def index sort = case params[:sort] @@ -19,7 +18,11 @@ def index elsif params[:ids].present? response = Activity.find_by_id(params[:ids], page: page, sort: sort) else - response = Activity.query(params[:query], uid: params[:doi_id], page: page, sort: sort, scroll_id: params[:scroll_id]) + response = Activity.query(params[:query], + uid: params[:doi_id] || params[:provider_id] || params[:client_id] || params[:repository_id], + page: page, + sort: sort, + scroll_id: params[:scroll_id]) end begin @@ -91,15 +94,6 @@ def show protected - def set_include - if params[:include].present? - @include = params[:include].split(",").map { |i| i.downcase.underscore.to_sym } - @include = @include & [:doi] - else - @include = [:doi] - end - end - def set_activity response = Activity.find_by_id(params[:id]) @activity = response.results.first diff --git a/app/models/activity.rb b/app/models/activity.rb index eed450b42..1fbb763e3 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -8,7 +8,7 @@ class Activity < Audited::Audit alias_attribute :doi_id, :uid alias_attribute :changes, :audited_changes - belongs_to :doi, foreign_key: :auditable_id + belongs_to :auditable, polymorphic: true def after_audit IndexJob.perform_later(self) @@ -20,161 +20,23 @@ def after_audit mapping dynamic: 'false' do indexes :id, type: :keyword indexes :auditable_id, type: :keyword - indexes :doi_id, type: :keyword indexes :uid, type: :keyword indexes :auditable_type, type: :keyword indexes :username, type: :keyword indexes :action, type: :keyword indexes :version, type: :keyword indexes :request_uuid, type: :keyword - indexes :changes, type: :object, properties: { - doi: { type: :keyword }, - url: { type: :text, fields: { keyword: { type: "keyword" }}}, - creators: { type: :object, properties: { - nameType: { type: :keyword }, - nameIdentifiers: { type: :object, properties: { - nameIdentifier: { type: :keyword }, - nameIdentifierScheme: { type: :keyword }, - schemeUri: { type: :keyword } - }}, - name: { type: :text }, - givenName: { type: :text }, - familyName: { type: :text }, - affiliation: { type: :object, properties: { - name: { type: :keyword }, - affiliationIdentifier: { type: :keyword }, - affiliationIdentifierScheme: { type: :keyword }, - schemeUri: { type: :keyword } - }}, - }}, - contributors: { type: :object, properties: { - nameType: { type: :keyword }, - nameIdentifiers: { type: :object, properties: { - nameIdentifier: { type: :keyword }, - nameIdentifierScheme: { type: :keyword }, - schemeUri: { type: :keyword } - }}, - name: { type: :text }, - givenName: { type: :text }, - familyName: { type: :text }, - affiliation: { type: :object, properties: { - name: { type: :keyword }, - affiliationIdentifier: { type: :keyword }, - affiliationIdentifierScheme: { type: :keyword }, - schemeUri: { type: :keyword } - }}, - contributorType: { type: :keyword } - }}, - titles: { type: :object, properties: { - title: { type: :text, fields: { keyword: { type: "keyword" }}}, - titleType: { type: :keyword }, - lang: { type: :keyword } - }}, - descriptions: { type: :object, properties: { - description: { type: :text }, - descriptionType: { type: :keyword }, - lang: { type: :keyword } - }}, - publisher: { type: :text, fields: { keyword: { type: "keyword" }}}, - publication_year: { type: :date, format: "yyyy", ignore_malformed: true }, - client_id: { type: :keyword }, - provider_id: { type: :keyword }, - identifiers: { type: :object, properties: { - identifierType: { type: :keyword }, - identifier: { type: :keyword } - }}, - related_identifiers: { type: :object, properties: { - relatedIdentifierType: { type: :keyword }, - relatedIdentifier: { type: :keyword }, - relationType: { type: :keyword }, - relatedMetadataScheme: { type: :keyword }, - schemeUri: { type: :keyword }, - schemeType: { type: :keyword }, - resourceTypeGeneral: { type: :keyword } - }}, - types: { type: :object, properties: { - resourceTypeGeneral: { type: :keyword }, - resourceType: { type: :keyword }, - schemaOrg: { type: :keyword }, - bibtex: { type: :keyword }, - citeproc: { type: :keyword }, - ris: { type: :keyword } - }}, - funding_references: { type: :object, properties: { - funderName: { type: :keyword }, - funderIdentifier: { type: :keyword }, - funderIdentifierType: { type: :keyword }, - awardNumber: { type: :keyword }, - awardUri: { type: :keyword }, - awardTitle: { type: :keyword } - }}, - dates: { type: :object, properties: { - date: { type: :text }, - dateType: { type: :keyword } - }}, - geo_locations: { type: :object, properties: { - geoLocationPoint: { type: :object }, - geoLocationBox: { type: :object }, - geoLocationPlace: { type: :keyword } - }}, - rights_list: { type: :object, properties: { - rights: { type: :keyword }, - rightsUri: { type: :keyword }, - lang: { type: :keyword }, - }}, - subjects: { type: :object, properties: { - subject: { type: :keyword }, - subjectScheme: { type: :keyword }, - schemeUri: { type: :keyword }, - valueUri: { type: :keyword }, - lang: { type: :keyword }, - }}, - container: { type: :object, properties: { - type: { type: :keyword }, - identifier: { type: :keyword }, - identifierType: { type: :keyword }, - title: { type: :keyword }, - volume: { type: :keyword }, - issue: { type: :keyword }, - firstPage: { type: :keyword }, - lastPage: { type: :keyword }, - }}, - content_url: { type: :keyword }, - version_info: { type: :keyword }, - formats: { type: :keyword }, - sizes: { type: :keyword }, - language: { type: :keyword }, - aasm_state: { type: :keyword }, - schema_version: { type: :keyword }, - metadata_version: { type: :keyword }, - source: { type: :keyword }, - landing_page: { type: :object, properties: { - checked: { type: :date, ignore_malformed: true }, - url: { type: :text, fields: { keyword: { type: "keyword" }}}, - status: { type: :integer }, - contentType: { type: :keyword }, - error: { type: :keyword }, - redirectCount: { type: :integer }, - redirectUrls: { type: :keyword }, - downloadLatency: { type: :scaled_float, scaling_factor: 100 }, - hasSchemaOrg: { type: :boolean }, - schemaOrgId: { type: :keyword }, - dcIdentifier: { type: :keyword }, - citationDoi: { type: :keyword }, - bodyHasPid: { type: :boolean } - }} - } + indexes :changes, type: :object indexes :created, type: :date, ignore_malformed: true # include parent objects - indexes :doi, type: :object + #indexes :doi, type: :object end def as_indexed_json(options={}) { "id" => id, "auditable_id" => auditable_id, - "doi_id" => doi_id, "uid" => uid, "auditable_type" => auditable_type, "username" => username, @@ -182,8 +44,10 @@ def as_indexed_json(options={}) "version" => version, "request_uuid" => request_uuid, "changes" => changes, - "created" => created, - "doi" => doi.present? ? doi.as_indexed_json : nil + "was_derived_from" => was_derived_from, + "was_attributed_to" => was_attributed_to, + "was_generated_by" => was_generated_by, + "created" => created } end @@ -322,6 +186,31 @@ def self.convert_affiliation_by_id(options={}) end def uid - doi.present? ? doi.uid : changes.to_h['doi'] + auditable.uid + end + + def url + Rails.env.production? ? "https://api.datacite.org" : "https://api.test.datacite.org" + end + + def was_derived_from + if auditable_type == "Doi" + handle_url = Rails.env.production? ? "https://doi.org/" : "https://handle.test.datacite.org/" + handle_url + uid + elsif auditable_type == "Provider" + url + "/providers/" + uid + elsif auditable_type == "Client" + url + "/repositories/" + uid + end + end + + def was_attributed_to + if username.present? + username.include?(".") ? url + "/repositories/" + username : url + "/providers/" + username + end + end + + def was_generated_by + url + "/activities/" + request_uuid end end diff --git a/app/models/client.rb b/app/models/client.rb index 19327b459..ebdd82408 100644 --- a/app/models/client.rb +++ b/app/models/client.rb @@ -1,4 +1,5 @@ class Client < ActiveRecord::Base + audited except: [:globus_uuid, :salesforce_id, :password, :updated, :comments, :experiments, :version, :doi_quota_allowed, :doi_quota_used] # include helper module for caching infrequently changing resources include Cacheable @@ -56,6 +57,7 @@ class Client < ActiveRecord::Base has_many :client_prefixes, foreign_key: :datacentre, dependent: :destroy has_many :prefixes, through: :client_prefixes has_many :provider_prefixes, through: :client_prefixes + has_many :activities, as: :auditable, dependent: :destroy before_validation :set_defaults before_create { self.created = Time.zone.now.utc.iso8601 } diff --git a/app/models/doi.rb b/app/models/doi.rb index 77e863229..66b0d25ee 100644 --- a/app/models/doi.rb +++ b/app/models/doi.rb @@ -80,7 +80,7 @@ class Doi < ActiveRecord::Base # has_many :part_of_events, -> { where target_relation_type_id: "part_of" }, class_name: "Event", primary_key: :doi, foreign_key: :target_doi, dependent: :destroy # has_many :version_events, -> { where source_relation_type_id: "versions" }, class_name: "Event", primary_key: :doi, foreign_key: :source_doi, dependent: :destroy # has_many :version_of_events, -> { where target_relation_type_id: "version_of" }, class_name: "Event", primary_key: :doi, foreign_key: :target_doi, dependent: :destroy - has_many :activities, foreign_key: :auditable_id, dependent: :destroy + has_many :activities, as: :auditable, dependent: :destroy # has_many :source_events, class_name: "Event", primary_key: :doi, foreign_key: :source_doi, dependent: :destroy # has_many :target_events, class_name: "Event", primary_key: :doi, foreign_key: :target_doi, dependent: :destroy diff --git a/app/models/provider.rb b/app/models/provider.rb index 7057b311a..b0a961196 100644 --- a/app/models/provider.rb +++ b/app/models/provider.rb @@ -1,6 +1,8 @@ require "countries" class Provider < ActiveRecord::Base + audited except: [:globus_uuid, :salesforce_id, :password, :updated, :experiments, :comments, :logo, :version, :doi_quota_allowed, :doi_quota_used] + # include helper module for caching infrequently changing resources include Cacheable @@ -72,6 +74,7 @@ class Provider < ActiveRecord::Base has_many :prefixes, through: :provider_prefixes has_many :consortium_organizations, class_name: "Provider", primary_key: "symbol", foreign_key: "consortium_id", inverse_of: :consortium belongs_to :consortium, class_name: "Provider", primary_key: "symbol", foreign_key: "consortium_id", inverse_of: :consortium_organizations, optional: true + has_many :activities, as: :auditable, dependent: :destroy before_validation :set_region, :set_defaults before_create { self.created = Time.zone.now.utc.iso8601 } diff --git a/app/serializers/activity_serializer.rb b/app/serializers/activity_serializer.rb index 90c6ebaed..d423f5621 100644 --- a/app/serializers/activity_serializer.rb +++ b/app/serializers/activity_serializer.rb @@ -6,23 +6,16 @@ class ActivitySerializer attributes "prov:wasGeneratedBy", "prov:generatedAtTime", "prov:wasDerivedFrom", "prov:wasAttributedTo", :action, :version, :changes - belongs_to :doi, record_type: :dois - attribute "prov:wasDerivedFrom" do |object| - url = Rails.env.production? ? "https://doi.org/" : "https://handle.test.datacite.org/" - url + object.uid + object.was_derived_from end attribute "prov:wasAttributedTo" do |object| - if object.username.present? - url = Rails.env.production? ? "https://api.datacite.org" : "https://api.test.datacite.org" - object.username.include?(".") ? url + "/clients/" + object.username : url + "/providers/" + object.username - end + object.was_attributed_to end attribute "prov:wasGeneratedBy" do |object| - url = Rails.env.production? ? "https://api.datacite.org" : "https://api.test.datacite.org" - "#{url}/activities/#{object.request_uuid}" + object.was_generated_by end attribute "prov:generatedAtTime" do |object| diff --git a/config/routes.rb b/config/routes.rb index d213cfa8f..4b5dcffac 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -72,11 +72,13 @@ resources :clients, constraints: { id: /.+/ } do resources :prefixes, constraints: { id: /.+/ } resources :dois, constraints: { id: /.+/ } + resources :activities end resources :repositories, constraints: { id: /.+/ } do resources :prefixes, constraints: { id: /.+/ } resources :dois, constraints: { id: /.+/ } + resources :activities end resources :client_prefixes, path: "client-prefixes" @@ -104,6 +106,7 @@ resources :organizations, constraints: { :id => /.+/ }, shallow: true resources :dois, constraints: { :id => /.+/ } resources :prefixes, constraints: { :id => /.+/ } + resources :activities end resources :providers, constraints: { :id => /.+/ } resources :repository_prefixes, path: "repository-prefixes" diff --git a/db/migrate/20190302161113_install_audited.rb b/db/migrate/20190302161113_install_audited.rb index 00d8dd304..504935816 100644 --- a/db/migrate/20190302161113_install_audited.rb +++ b/db/migrate/20190302161113_install_audited.rb @@ -2,21 +2,23 @@ class InstallAudited < ActiveRecord::Migration[5.2] def self.up - create_table :audits, force: true do |t| - t.column :auditable_id, :integer - t.column :auditable_type, :string - t.column :associated_id, :integer - t.column :associated_type, :string - t.column :user_id, :integer - t.column :user_type, :string - t.column :username, :string - t.column :action, :string - t.column :audited_changes, :json - t.column :version, :integer, default: 0 - t.column :comment, :string - t.column :remote_address, :string - t.column :request_uuid, :string - t.column :created_at, :datetime, limit: 3 + safety_assured do + create_table :audits, force: true do |t| + t.column :auditable_id, :integer + t.column :auditable_type, :string + t.column :associated_id, :integer + t.column :associated_type, :string + t.column :user_id, :integer + t.column :user_type, :string + t.column :username, :string + t.column :action, :string + t.column :audited_changes, :json + t.column :version, :integer, default: 0 + t.column :comment, :string + t.column :remote_address, :string + t.column :request_uuid, :string + t.column :created_at, :datetime, limit: 3 + end end add_index :audits, [:auditable_type, :auditable_id, :version], name: "auditable_index" @@ -27,6 +29,8 @@ def self.up end def self.down - drop_table :audits + safety_assured do + drop_table :audits + end end end diff --git a/spec/factories/default.rb b/spec/factories/default.rb index 9ab62ef75..e8063a818 100644 --- a/spec/factories/default.rb +++ b/spec/factories/default.rb @@ -311,7 +311,7 @@ end factory :activity do - association :doi, factory: :doi, strategy: :create + association :auditable, factory: :doi, strategy: :create end factory :event do diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 6b9464786..5e17bcd3c 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -8,7 +8,7 @@ it "activity exists" do expect(doi.activities.length).to eq(1) activity = doi.activities.first - expect(activity.uid).to eq(doi.uid) + expect(activity.auditable.uid).to eq(doi.uid) # expect(activity.username).to eq(2) expect(activity.request_uuid).to be_present expect(activity.changes["aasm_state"]).to eq("draft") @@ -25,10 +25,68 @@ expect(doi.activities.length).to eq(2) activity = doi.activities.last - expect(activity.uid).to eq(doi.uid) + expect(activity.auditable.uid).to eq(doi.uid) # expect(activity.username).to eq(2) expect(activity.request_uuid).to be_present expect(activity.changes).to eq("aasm_state"=>["draft", "findable"]) end end + + context "create provider" do + let(:provider) { create(:provider) } + + it "activity exists" do + expect(provider.activities.length).to eq(1) + activity = provider.activities.first + expect(activity.auditable.uid).to eq(provider.uid) + + expect(activity.request_uuid).to be_present + expect(activity.changes["non_profit_status"]).to eq("non-profit") + expect(activity.changes["display_name"]).to eq("My provider") + end + end + + context "update provider" do + let(:provider) { create(:provider) } + + it "activity exists" do + provider.update(non_profit_status: "for-profit") + + expect(provider.activities.length).to eq(2) + activity = provider.activities.last + expect(activity.auditable.uid).to eq(provider.uid) + + expect(activity.request_uuid).to be_present + expect(activity.changes).to eq("non_profit_status" => ["non-profit", "for-profit"]) + end + end + + context "create client" do + let(:client) { create(:client) } + + it "activity exists" do + expect(client.activities.length).to eq(1) + activity = client.activities.first + expect(activity.auditable.uid).to eq(client.uid) + + expect(activity.request_uuid).to be_present + expect(activity.changes["client_type"]).to eq("repository") + expect(activity.changes["name"]).to eq("My data center") + end + end + + context "update client" do + let(:client) { create(:client) } + + it "activity exists" do + client.update(client_type: "periodical") + + expect(client.activities.length).to eq(2) + activity = client.activities.last + expect(activity.auditable.uid).to eq(client.uid) + + expect(activity.request_uuid).to be_present + expect(activity.changes).to eq("client_type" => ["repository", "periodical"]) + end + end end diff --git a/spec/models/provider_spec.rb b/spec/models/provider_spec.rb index 68af72a13..f4574acc8 100644 --- a/spec/models/provider_spec.rb +++ b/spec/models/provider_spec.rb @@ -203,4 +203,16 @@ expect(provider.cumulative_years).to eq([]) end end + + describe "activities" do + subject { build(:provider) } + + it "is valid" do + expect(subject.save).to be true + expect(subject.errors.details).to be_empty + expect(subject.activities.length).to eq(1) + activity = subject.activities.first + expect(activity.changes["non_profit_status"]).to eq("non-profit") + end + end end