93 lines
2.6 KiB
Ruby
93 lines
2.6 KiB
Ruby
# This service lists the download link from a remote source based on the
|
|
# oids provided
|
|
module Projects
|
|
module LfsPointers
|
|
class LfsDownloadLinkListService < BaseService
|
|
DOWNLOAD_ACTION = 'download'.freeze
|
|
|
|
DownloadLinksError = Class.new(StandardError)
|
|
DownloadLinkNotFound = Class.new(StandardError)
|
|
|
|
attr_reader :remote_uri
|
|
|
|
def initialize(project, remote_uri: nil)
|
|
super(project)
|
|
|
|
@remote_uri = remote_uri
|
|
end
|
|
|
|
# This method accepts two parameters:
|
|
# - oids: hash of oids to query. The structure is { lfs_file_oid => lfs_file_size }
|
|
#
|
|
# Returns a hash with the structure { lfs_file_oids => download_link }
|
|
def execute(oids)
|
|
return {} unless project&.lfs_enabled? && remote_uri && oids.present?
|
|
|
|
get_download_links(oids)
|
|
end
|
|
|
|
private
|
|
|
|
def get_download_links(oids)
|
|
response = Gitlab::HTTP.post(remote_uri,
|
|
body: request_body(oids),
|
|
headers: headers)
|
|
|
|
raise DownloadLinksError, response.message unless response.success?
|
|
|
|
parse_response_links(response['objects'])
|
|
end
|
|
|
|
def parse_response_links(objects_response)
|
|
objects_response.each_with_object({}) do |entry, link_list|
|
|
begin
|
|
oid = entry['oid']
|
|
link = entry.dig('actions', DOWNLOAD_ACTION, 'href')
|
|
|
|
raise DownloadLinkNotFound unless link
|
|
|
|
link_list[oid] = add_credentials(link)
|
|
rescue DownloadLinkNotFound, URI::InvalidURIError
|
|
Rails.logger.error("Link for Lfs Object with oid #{oid} not found or invalid.")
|
|
end
|
|
end
|
|
end
|
|
|
|
def request_body(oids)
|
|
{
|
|
operation: DOWNLOAD_ACTION,
|
|
objects: oids.map { |oid, size| { oid: oid, size: size } }
|
|
}.to_json
|
|
end
|
|
|
|
def headers
|
|
{
|
|
'Accept' => LfsRequest::CONTENT_TYPE,
|
|
'Content-Type' => LfsRequest::CONTENT_TYPE
|
|
}.freeze
|
|
end
|
|
|
|
def add_credentials(link)
|
|
uri = URI.parse(link)
|
|
|
|
if should_add_credentials?(uri)
|
|
uri.user = remote_uri.user
|
|
uri.password = remote_uri.password
|
|
end
|
|
|
|
uri.to_s
|
|
end
|
|
|
|
# The download link can be a local url or an object storage url
|
|
# If the download link has the some host as the import url then
|
|
# we add the same credentials because we may need them
|
|
def should_add_credentials?(link_uri)
|
|
url_credentials? && link_uri.host == remote_uri.host
|
|
end
|
|
|
|
def url_credentials?
|
|
remote_uri.user.present? || remote_uri.password.present?
|
|
end
|
|
end
|
|
end
|
|
end
|