diff --git a/lib/gitlab.rb b/lib/gitlab.rb index 000f0df69..005ec005d 100644 --- a/lib/gitlab.rb +++ b/lib/gitlab.rb @@ -8,6 +8,7 @@ require 'gitlab/headers/total' require 'gitlab/paginated_response' require 'gitlab/file_response' +require 'gitlab/header_response' require 'gitlab/request' require 'gitlab/api' require 'gitlab/client' diff --git a/lib/gitlab/client/repository_files.rb b/lib/gitlab/client/repository_files.rb index 77782e368..0f17f6dba 100644 --- a/lib/gitlab/client/repository_files.rb +++ b/lib/gitlab/client/repository_files.rb @@ -56,6 +56,26 @@ def get_file(project, file_path, ref) }) end + # Gets a repository file metadata. + # + # @example + # Gitlab.get_file_metadata(42, "README.md", "master") + # + # @param [Integer, String] project The ID or name of a project. + # @param [String] file_path The full path of the file. + # @param [String] ref The name of branch, tag or commit. + # @return [Gitlab::ObjectifiedHash] + def get_file_metadata(project, file_path, ref) + head( + "/projects/#{url_encode project}/repository/files/#{url_encode file_path}", + query: { ref: ref }, + format: nil, + parser: proc do |_body, _format| + ::Gitlab::HeaderResponse.new + end + ) + end + # Creates a new repository file. # # @example diff --git a/lib/gitlab/header_response.rb b/lib/gitlab/header_response.rb new file mode 100644 index 000000000..ef002a663 --- /dev/null +++ b/lib/gitlab/header_response.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Gitlab + class HeaderResponse + GITLAB_HEADER = 'x-gitlab-' + + def initialize + @headers = {} + end + + def empty? + headers.empty? + end + + def to_hash + headers + end + alias to_h to_hash + + def method_missing(method, *args, &) + if headers.key?(method) + headers[method] + else + super + end + end + + def respond_to_missing?(method, include_private = false) + headers.key?(method) || super + end + + def inspect + "#<#{self.class}:#{object_id} #{headers}>" + end + + def parse_headers!(headers) + @headers = headers.select { |header, _value| header.include?(GITLAB_HEADER) } + .map { |header, value| { header_key(header) => value[0] } } + .reduce(&:merge) + end + + private + + attr_reader :headers + + def header_key(header) + header.gsub(GITLAB_HEADER, '').tr('-', '_').to_sym + end + end +end diff --git a/lib/gitlab/request.rb b/lib/gitlab/request.rb index caa5525e9..3a1041d31 100644 --- a/lib/gitlab/request.rb +++ b/lib/gitlab/request.rb @@ -8,6 +8,8 @@ module Gitlab class Request include HTTParty + SUPPORTED_METHODS = %i[head get post put patch delete].freeze + format :json maintain_method_across_redirects true headers 'Accept' => 'application/json', 'Content-Type' => 'application/x-www-form-urlencoded' @@ -39,7 +41,7 @@ def self.decode(response) raise Error::Parsing, 'The response is not a valid JSON' end - %w[get post put patch delete].each do |method| + SUPPORTED_METHODS.each do |method| define_method method do |path, options = {}| params = options.dup diff --git a/spec/gitlab/client/repository_files_spec.rb b/spec/gitlab/client/repository_files_spec.rb index cdecb6e6f..2f1b47ffd 100644 --- a/spec/gitlab/client/repository_files_spec.rb +++ b/spec/gitlab/client/repository_files_spec.rb @@ -50,6 +50,17 @@ end end + describe '.get_file_metadata' do + before do + stub_head('/projects/3/repository/files/README%2Emd?ref=master', 'empty') + @file = Gitlab.get_file_metadata(3, 'README.md', 'master') + end + + it 'gets the correct resource' do + expect(a_head('/projects/3/repository/files/README%2Emd?ref=master')).to have_been_made + end + end + describe '.create_file' do let(:api_path) { '/projects/3/repository/files/path' } diff --git a/spec/gitlab/header_response_spec.rb b/spec/gitlab/header_response_spec.rb new file mode 100644 index 000000000..f47ab4eee --- /dev/null +++ b/spec/gitlab/header_response_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::HeaderResponse do + subject(:resp) { described_class.new } + + let(:headers) do + { + 'x-gitlab-blob-size' => ['42'], + 'x-gitlab-commit-id' => ['foobar'], + 'content-type' => ['application/json'] + } + end + + describe '.empty?' do + it 'returns false' do + expect(resp).to be_empty + end + end + + describe '.to_hash' do + it 'empty hash after init' do + expect(resp.to_hash).to eq({}) + end + end + + describe '.parse_headers!' do + subject(:resp_with_h) do + resp.parse_headers!(headers) + resp + end + + it 'parses headers' do + expect(resp_with_h.to_h).to eq( + { + blob_size: '42', + commit_id: 'foobar' + } + ) + end + + it 'handles getters' do + expect(resp_with_h.commit_id).to eq('foobar') + end + + it 'nonexistant header' do + expect { resp_with_h.expires_in }.to raise_error(NoMethodError) + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index bc0698238..39bad92c5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -36,7 +36,7 @@ def load_fixture(name) Kernel.srand config.seed end -%i[get post put delete patch].each do |method| +Gitlab::Request::SUPPORTED_METHODS.each do |method| define_method "stub_#{method}" do |path, fixture, status_code = 200| stub_request(method, "#{Gitlab.endpoint}#{path}") .with(headers: { 'PRIVATE-TOKEN' => Gitlab.private_token })