app/models/globus_destination.rb
# frozen_string_literal: true
# Model representing a Globus destination that can be used as a staging location.
class GlobusDestination < ApplicationRecord
belongs_to :batch_context, optional: true
belongs_to :user
after_initialize :set_directory
scope :active, -> { where(deleted_at: nil) }
scope :older_than, ->(time) { where(created_at: ...time) }
# A helper method to find potential Globus Destinations to cleanup
def self.find_stale(timeframe)
active.older_than(timeframe)
end
# A helper to look up the GlobusDestination object using a Globus URL.
# @param url [String] a Globus URL
# @return [GlobusDestination, nil] the GlobusDestination found or nil
def self.find_with_globus_url(url)
path = extract_path(url)
return unless path
_, sunet_id, directory = path.split('/')
user = User.find_by(sunet_id:)
find_by(user:, directory:)
end
# A helper to look up the GlobusDestination object using a Globus destination path.
# @param url [String] a Globus path
# @return [GlobusDestination, nil] the GlobusDestination found or nil
def self.find_with_globus_path(path)
return unless path.start_with?(Settings.globus.directory)
# the second delete_prefix is just in case the configured Settings.globus.directory has no trailing slash
sunet_id, directory = path.delete_prefix(Settings.globus.directory).delete_prefix('/').split('/')
user = User.find_by(sunet_id:)
find_by(user:, directory:)
end
# Extract the path from either destination_path or origin_path depending on
# whether origin_id or destination_id has the configured globus-endpoint-id.
# This allows users to paste in a Globus viewer URL that they may have been
# emailed which flips around the destination_path to the origin_path, and also
# will find when they may have flipped the origin/destination in the viewer.
# @param url [String] a Globus URL
# @return [String, nil] the path value, or nil if not found
def self.extract_path(url)
uri = URI.parse(url)
return unless uri.query
params = CGI.parse(uri.query)
endpoint_param = if params['origin_id']&.first == Settings.globus.endpoint_id
'origin_path'
else
'destination_path'
end
params[endpoint_param]&.first
end
# Creates a URL for globus, including the path to the user's destination directory
def url
"https://app.globus.org/file-manager?&destination_id=#{Settings.globus.endpoint_id}&destination_path=#{destination_path}"
end
# Get the directory within globus including user directory in the format /sunet/datetime
def destination_path
"/#{user.sunet_id}/#{directory}"
end
# Get the path on preassembly filesystem to staged files
def staging_location
"#{Settings.globus.directory}#{destination_path}"
end
# Set the default directory name using current time (when not already set)
def set_directory
self.directory = DateTime.now.strftime('%Y-%m-%d-%H-%M-%S-%L') unless directory
end
end