debian-mirror-gitlab/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb

105 lines
3.3 KiB
Ruby
Raw Normal View History

2018-11-18 11:00:15 +05:30
# frozen_string_literal: true
2018-11-08 19:23:39 +05:30
# 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 }
#
2019-07-31 22:56:46 +05:30
# Returns an array of LfsDownloadObject
2018-11-08 19:23:39 +05:30
def execute(oids)
2019-07-31 22:56:46 +05:30
return [] unless project&.lfs_enabled? && remote_uri && oids.present?
2018-11-08 19:23:39 +05:30
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?
2019-07-31 22:56:46 +05:30
# Since the LFS Batch API may return a Content-Ttpe of
# application/vnd.git-lfs+json
# (https://github.com/git-lfs/git-lfs/blob/master/docs/api/batch.md#requests),
# HTTParty does not know this is actually JSON.
data = JSON.parse(response.body)
raise DownloadLinksError, "LFS Batch API did return any objects" unless data.is_a?(Hash) && data.key?('objects')
parse_response_links(data['objects'])
rescue JSON::ParserError
raise DownloadLinksError, "LFS Batch API response is not JSON"
2018-11-08 19:23:39 +05:30
end
def parse_response_links(objects_response)
2019-02-02 18:00:53 +05:30
objects_response.each_with_object([]) do |entry, link_list|
2019-07-07 11:18:12 +05:30
link = entry.dig('actions', DOWNLOAD_ACTION, 'href')
2018-11-08 19:23:39 +05:30
2019-07-07 11:18:12 +05:30
raise DownloadLinkNotFound unless link
2018-11-08 19:23:39 +05:30
2019-07-07 11:18:12 +05:30
link_list << LfsDownloadObject.new(oid: entry['oid'],
size: entry['size'],
link: add_credentials(link))
rescue DownloadLinkNotFound, Addressable::URI::InvalidURIError
log_error("Link for Lfs Object with oid #{entry['oid']} not found or invalid.")
2018-11-08 19:23:39 +05:30
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)
2019-02-02 18:00:53 +05:30
uri = Addressable::URI.parse(link)
2018-11-08 19:23:39 +05:30
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