Skip to content

Commit

Permalink
Merge pull request #493 from datacite/feature_new_repository_ids
Browse files Browse the repository at this point in the history
Feature  Repository Transfer
  • Loading branch information
kjgarza authored May 12, 2020
2 parents 190252c + 2087ae5 commit 918a26f
Show file tree
Hide file tree
Showing 17 changed files with 346 additions and 26 deletions.
27 changes: 18 additions & 9 deletions app/controllers/clients_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ def index
elsif params[:ids].present?
response = Client.find_by_id(params[:ids], page: page, sort: sort)
else
response = Client.query(params[:query],
response = Client.query(
params[:query],
year: params[:year],
from_date: params[:from_date],
until_date: params[:until_date],
Expand All @@ -33,9 +34,10 @@ def index
software: params[:software],
certificate: params[:certificate],
repository_type: params[:repository_type],
client_type: params[:client_type],
page: page,
sort: sort)
client_type: params[:client_type],
page: page,
sort: sort,
)
end

begin
Expand Down Expand Up @@ -123,11 +125,18 @@ def create
end

def update
if @client.update(safe_params)
options = {}
options[:is_collection] = false
options[:params] = { current_ability: current_ability }

options = {}
options[:is_collection] = false
options[:params] = { current_ability: current_ability }

if params.dig(:data, :attributes, :mode) == "transfer"
# only update provider_id
authorize! :transfer, @client

@client.transfer(safe_params.slice(:target_id))
render json: ClientSerializer.new(@client, options).serialized_json, status: :ok
elsif @client.update(safe_params)

render json: ClientSerializer.new(@client, options).serialized_json, status: :ok
else
Rails.logger.error @client.errors.inspect
Expand Down
15 changes: 11 additions & 4 deletions app/controllers/repositories_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,17 @@ def create
end

def update
if @client.update_attributes(safe_params)
options = {}
options[:is_collection] = false
options[:params] = { current_ability: current_ability }
options = {}
options[:is_collection] = false
options[:params] = { current_ability: current_ability }

if params.dig(:data, :attributes, :mode) == "transfer"
# only update provider_id
authorize! :transfer, @client

@client.transfer(safe_params.slice(:target_id))
render json: RepositorySerializer.new(@client, options).serialized_json, status: :ok
elsif @client.update(safe_params)

render json: RepositorySerializer.new(@client, options).serialized_json, status: :ok
else
Expand Down
22 changes: 22 additions & 0 deletions app/jobs/transfer_client_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class TransferClientJob < ActiveJob::Base
queue_as :lupo_background

def perform(client, options = {})
symbol = client.symbol

if client.present? && options[:target_id].present?
options = {
filter: { client_id: symbol.downcase },
label: "[ClientTransfer]",
job_name: "UpdateProviderIdJob",
target_id: options[:target_id],
}

Doi.loop_through_dois(options)

Rails.logger.info "[Transfer] DOIs updating has started for #{symbol} to #{options[:target_id]}."
else
Rails.logger.error "[Transfer] Error updating DOIs " + symbol + ": not found"
end
end
end
22 changes: 22 additions & 0 deletions app/jobs/update_provider_id_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class UpdateProviderIdJob < ActiveJob::Base
queue_as :lupo_transfer

# retry_on ActiveRecord::RecordNotFound, wait: 10.seconds, attempts: 3
# retry_on Faraday::TimeoutError, wait: 10.minutes, attempts: 3

# discard_on ActiveJob::DeserializationError

def perform(doi_id, options = {})
doi = Doi.where(doi: doi_id).first

if doi.present? && options[:target_id].present?
doi.__elasticsearch__.index_document

Rails.logger.info "[Transfer] updated DOI #{doi.doi}."
elsif doi.present?
Rails.logger.error "[Transfer] Error updateding DOI " + doi_id + ": no target client"
else
Rails.logger.error "[Transfer] Error updateding DOI " + doi_id + ": not found"
end
end
end
2 changes: 2 additions & 0 deletions app/models/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def initialize(user)
can [:manage], Client do |client|
client.provider && user.provider_id.casecmp(client.provider.consortium_id)
end
cannot [:transfer], Client
can [:manage], ClientPrefix #, :client_id => user.provider_id

# if Flipper[:delete_doi].enabled?(user)
Expand All @@ -51,6 +52,7 @@ def initialize(user)
can [:update, :read, :read_billing_information], Provider, symbol: user.provider_id.upcase
can [:manage], ProviderPrefix, provider_id: user.provider_id
can [:manage], Client, provider_id: user.provider_id
cannot [:transfer], Client
can [:manage], ClientPrefix #, :client_id => user.provider_id

# if Flipper[:delete_doi].enabled?(user)
Expand Down
49 changes: 49 additions & 0 deletions app/models/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,55 @@ def target_id=(value)
Doi.transfer(client_id: symbol.downcase, target_id: target.id)
end

def transfer(options = {})
if options[:target_id].blank?
Rails.logger.error "[Transfer] No target provider provided."
return nil
end

target_provider = Provider.where(symbol: options[:target_id]).first

if target_provider.blank?
Rails.logger.error "[Transfer] Provider doesn't exist."
return nil
end

unless ["direct_member", "consortium_organization"].include?(target_provider.member_type)
Rails.logger.error "[Transfer] Consortiums and Members-only cannot have repositories."
return nil
end

# Transfer client
update_attribute(:allocator, target_provider.id)

# transfer prefixes
transfer_prefixes(target_provider.symbol)

# Update DOIs
TransferClientJob.perform_later(self, target_id: options[:target_id])
end

def transfer_prefixes(target_id)
# These prefixes are used by multiple clients
prefixes_to_keep = ["10.4124", "10.4225", "10.4226", "10.4227"]

# delete all associated prefixes
associated_prefixes = prefixes.reject{ |prefix| prefixes_to_keep.include?(prefix.uid)}
prefix_ids = associated_prefixes.pluck(:id)
prefixes_names = associated_prefixes.pluck(:uid)

if prefix_ids.present?
response = ProviderPrefix.where("prefix_id IN (?)", prefix_ids).destroy_all
puts "#{response.count} provider prefixes deleted."
end

# Assign prefix(es) to provider
prefixes_names.each do |prefix|
ProviderPrefix.create(provider_id: target_id, prefix_id: prefix)
puts "Provider prefix for provider #{target_id} and prefix #{prefix} created."
end
end

def service_contact_email
service_contact.fetch("email",nil) if service_contact.present?
end
Expand Down
14 changes: 7 additions & 7 deletions app/models/concerns/authenticable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def get_payload(uid: nil, user: nil, password: nil)
# we only need password for clients registering DOIs in the handle system
if uid.include? "."
payload.merge!(
"provider_id" => uid.split(".", 2).first,
"provider_id" => user.provider_id,
"client_id" => uid,
"password" => password,
)
Expand Down Expand Up @@ -298,15 +298,15 @@ def get_payload(uid: nil, user: nil, password: nil)

# we only need password for clients registering DOIs in the handle system
if uid.include? "."
payload.merge!({
"provider_id" => uid.split(".", 2).first,
payload.merge!(
"provider_id" => user.provider_id,
"client_id" => uid,
"password" => password
})
"password" => password,
)
elsif uid != "admin"
payload.merge!({
payload.merge!(
"provider_id" => uid
})
)
end

payload
Expand Down
4 changes: 2 additions & 2 deletions app/models/doi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1780,7 +1780,7 @@ def self.transfer(options={})
# +job_name+:: Acive Job class name of the Job that would be executed on every matched results
def self.loop_through_dois(options)
size = (options[:size] || 1000).to_i
cursor = [options[:from_id], options[:until_id]]
cursor = [options[:from_id] || Doi.minimum(:id).to_i, options[:until_id] || Doi.maximum(:id).to_i]
filter = options[:filter] || {}
label = options[:label] || ""
job_name = options[:job_name] || ""
Expand All @@ -1802,7 +1802,7 @@ def self.loop_through_dois(options)

ids = response.results.results.map(&:uid)
ids.each do |id|
Object.const_get(job_name).perform_later(id, filter)
Object.const_get(job_name).perform_later(id, options)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion app/models/event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ def self.label_state_event(event)
# +job_name+:: Acive Job class name of the Job that would be executed on every matched results
def self.loop_through_events(options)
size = (options[:size] || 1000).to_i
cursor = [options[:from_id], options[:until_id]]
cursor = [options[:from_id] || Doi.minimum(:id).to_i, options[:until_id] || Doi.maximum(:id).to_i]
filter = options[:filter] || {}
label = options[:label] || ""
job_name = options[:job_name] || ""
Expand Down
3 changes: 2 additions & 1 deletion db/seeds/development/base.seeds.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
client = Client.where(symbol: "DATACITE.TEST").first || FactoryBot.create(:client, provider: provider, symbol: ENV["MDS_USERNAME"], password: ENV["MDS_PASSWORD"])
if Prefix.where(uid: "10.14454").blank?
prefix = FactoryBot.create(:prefix, uid: "10.14454")
FactoryBot.create(:client_prefix, client_id: client.id, prefix_id: prefix.id)
FactoryBot.create(:provider_prefix, provider_id: provider.symbol, prefix_id: prefix.uid)
FactoryBot.create(:client_prefix, client_id: client.symbol, prefix_id: prefix.uid)
end
dois = FactoryBot.create_list(:doi, 10, client: client, state: "findable")
FactoryBot.create_list(:event_for_datacite_related, 3, obj_id: dois.first.doi)
Expand Down
17 changes: 17 additions & 0 deletions db/seeds/development/consortium_transfer.seeds.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require "factory_bot_rails"

fail "Seed tasks can only be used in the development enviroment" if Rails.env.production?

after "development:base" do

provider = Provider.where(symbol: "QUECHUA").first || FactoryBot.create(:provider, symbol: "QUECHUA")
client = Client.where(symbol: "QUECHUA.TEXT").first || FactoryBot.create(:client, provider: provider, symbol: "QUECHUA.TEXT", password: ENV["MDS_PASSWORD"])
if Prefix.where(uid: "10.14459").blank?
prefix = FactoryBot.create(:prefix, uid: "10.14459")
FactoryBot.create(:provider_prefix, provider_id: provider.symbol, prefix_id: prefix.uid)
FactoryBot.create(:client_prefix, client_id: client.symbol, prefix_id: prefix.uid)
end
dois = FactoryBot.create_list(:doi, 10, client: client, state: "findable")
FactoryBot.create_list(:event_for_datacite_related, 3, obj_id: dois.first.doi)
FactoryBot.create_list(:event_for_datacite_usage, 2, obj_id: dois.first.doi)
end
8 changes: 7 additions & 1 deletion spec/concerns/authenticable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,13 @@

describe 'decode_auth_param' do
it "works" do
expect(subject.decode_auth_param(username: subject.symbol, password: 12345)).to eq("uid"=>subject.symbol.downcase, "name"=>subject.name, "email"=>subject.system_email, "password" => "12345", "role_id"=>"client_admin", "provider_id"=>subject.symbol.downcase.split(".").first, "client_id"=>subject.symbol.downcase)
expect(subject.decode_auth_param(username: subject.symbol, password: 12345)).to eq("uid"=>subject.symbol.downcase, "name"=>subject.name, "email"=>subject.system_email, "password" => "12345", "role_id"=>"client_admin", "provider_id"=>subject.provider_id, "client_id"=>subject.symbol.downcase)
end
end

describe 'get_payload' do
it "works" do
expect(subject.get_payload(uid: subject.symbol.downcase, user: subject, password: 12345)).to eq("uid"=>subject.symbol.downcase, "name"=>subject.name, "email"=>subject.system_email, "password" => 12345, "role_id"=>"client_admin", "provider_id"=>subject.provider_id, "client_id"=>subject.symbol.downcase)
end
end
end
9 changes: 9 additions & 0 deletions spec/models/ability_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
it { is_expected.not_to be_able_to(:create, client) }
it { is_expected.not_to be_able_to(:update, client) }
it { is_expected.not_to be_able_to(:destroy, client) }
it { is_expected.not_to be_able_to(:transfer, client) }

it { is_expected.not_to be_able_to(:read, prefix) }
it { is_expected.not_to be_able_to(:create, prefix) }
Expand Down Expand Up @@ -65,6 +66,7 @@
it { is_expected.not_to be_able_to(:create, client) }
it { is_expected.to be_able_to(:update, client) }
it { is_expected.not_to be_able_to(:destroy, client) }
it { is_expected.not_to be_able_to(:transfer, client) }

it { is_expected.not_to be_able_to(:read, prefix) }
it { is_expected.not_to be_able_to(:create, prefix) }
Expand Down Expand Up @@ -97,6 +99,7 @@
it { is_expected.not_to be_able_to(:create, client) }
it { is_expected.not_to be_able_to(:update, client) }
it { is_expected.not_to be_able_to(:destroy, client) }
it { is_expected.not_to be_able_to(:transfer, client) }

it { is_expected.not_to be_able_to(:read, prefix) }
it { is_expected.not_to be_able_to(:create, prefix) }
Expand Down Expand Up @@ -129,6 +132,7 @@
it { is_expected.to be_able_to(:create, client) }
it { is_expected.to be_able_to(:update, client) }
it { is_expected.to be_able_to(:destroy, client) }
it { is_expected.not_to be_able_to(:transfer, client) }

it { is_expected.not_to be_able_to(:read, prefix) }
it { is_expected.not_to be_able_to(:create, prefix) }
Expand Down Expand Up @@ -161,6 +165,7 @@
it { is_expected.to be_able_to(:create, provider) }
it { is_expected.to be_able_to(:update, provider) }
it { is_expected.to be_able_to(:destroy, provider) }
it { is_expected.not_to be_able_to(:transfer, client) }

it { is_expected.to be_able_to(:read, client) }
it { is_expected.to be_able_to(:create, client) }
Expand Down Expand Up @@ -198,6 +203,7 @@
it { is_expected.not_to be_able_to(:create, client) }
it { is_expected.not_to be_able_to(:update, client) }
it { is_expected.not_to be_able_to(:destroy, client) }
it { is_expected.not_to be_able_to(:transfer, client) }

it { is_expected.not_to be_able_to(:read, prefix) }
it { is_expected.not_to be_able_to(:create, prefix) }
Expand All @@ -223,6 +229,7 @@
it { is_expected.to be_able_to(:create, provider) }
it { is_expected.to be_able_to(:update, provider) }
it { is_expected.to be_able_to(:destroy, provider) }
it { is_expected.to be_able_to(:transfer, client) }

it { is_expected.to be_able_to(:read, client) }
it { is_expected.to be_able_to(:create, client) }
Expand Down Expand Up @@ -250,6 +257,7 @@
it { is_expected.not_to be_able_to(:create, client) }
it { is_expected.not_to be_able_to(:update, client) }
it { is_expected.not_to be_able_to(:destroy, client) }
it { is_expected.not_to be_able_to(:transfer, client) }

it { is_expected.to be_able_to(:read, doi) }
it { is_expected.not_to be_able_to(:transfer, doi) }
Expand All @@ -269,6 +277,7 @@
it { is_expected.not_to be_able_to(:create, client) }
it { is_expected.not_to be_able_to(:update, client) }
it { is_expected.not_to be_able_to(:destroy, client) }
it { is_expected.not_to be_able_to(:transfer, client) }

it { is_expected.to be_able_to(:read, doi) }
it { is_expected.not_to be_able_to(:transfer, doi) }
Expand Down
Loading

0 comments on commit 918a26f

Please sign in to comment.