BigW Consortium Gitlab

commits.rb 6.44 KB
Newer Older
1 2 3
require 'mime/types'

module API
4
  # Projects commits API
5 6 7 8
  class Commits < Grape::API
    before { authenticate! }
    before { authorize! :download_code, user_project }

9 10 11
    params do
      requires :id, type: String, desc: 'The ID of a project'
    end
12
    resource :projects do
13 14 15 16 17 18 19 20 21
      desc 'Get a project repository commits' do
        success Entities::RepoCommit
      end
      params do
        optional :ref_name, type: String, desc: 'The name of a repository branch or tag, if not given the default branch is used'
        optional :since,    type: String, desc: 'Only commits after or in this date will be returned'
        optional :until,    type: String, desc: 'Only commits before or in this date will be returned'
        optional :page,     type: Integer, default: 0, desc: 'The page for pagination'
        optional :per_page, type: Integer, default: 20, desc: 'The number of results per page'
22
        optional :path,     type: String, desc: 'The file path'
23
      end
24
      get ":id/repository/commits" do
25
        # TODO remove the next line for 9.0, use DateTime type in the params block
26 27
        datetime_attributes! :since, :until

28
        ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
29 30 31
        offset = params[:page] * params[:per_page]

        commits = user_project.repository.commits(ref,
32
                                                  path: params[:path],
33 34 35 36
                                                  limit: params[:per_page],
                                                  offset: offset,
                                                  after: params[:since],
                                                  before: params[:until])
37 38 39 40

        present commits, with: Entities::RepoCommit
      end

Marc Siegfriedt committed
41
      desc 'Commit multiple file changes as one commit' do
42
        success Entities::RepoCommitDetail
Marc Siegfriedt committed
43 44 45 46 47 48 49 50 51 52 53 54 55
        detail 'This feature was introduced in GitLab 8.13'
      end
      params do
        requires :id, type: Integer, desc: 'The project ID'
        requires :branch_name, type: String, desc: 'The name of branch'
        requires :commit_message, type: String, desc: 'Commit message'
        requires :actions, type: Array, desc: 'Actions to perform in commit'
        optional :author_email, type: String, desc: 'Author email for commit'
        optional :author_name, type: String, desc: 'Author name for commit'
      end
      post ":id/repository/commits" do
        authorize! :push_code, user_project

56
        attrs = declared_params
Marc Siegfriedt committed
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
        attrs[:source_branch] = attrs[:branch_name]
        attrs[:target_branch] = attrs[:branch_name]
        attrs[:actions].map! do |action|
          action[:action] = action[:action].to_sym
          action[:file_path].slice!(0) if action[:file_path] && action[:file_path].start_with?('/')
          action[:previous_path].slice!(0) if action[:previous_path] && action[:previous_path].start_with?('/')
          action
        end

        result = ::Files::MultiService.new(user_project, current_user, attrs).execute

        if result[:status] == :success
          commit_detail = user_project.repository.commits(result[:result], limit: 1).first
          present commit_detail, with: Entities::RepoCommitDetail
        else
          render_api_error!(result[:message], 400)
        end
      end

76 77 78 79 80 81 82
      desc 'Get a specific commit of a project' do
        success Entities::RepoCommitDetail
        failure [[404, 'Not Found']]
      end
      params do
        requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
      end
83
      get ":id/repository/commits/:sha" do
84 85
        commit = user_project.commit(params[:sha])

86
        not_found! "Commit" unless commit
87

88 89 90
        present commit, with: Entities::RepoCommitDetail
      end

91 92 93 94 95 96
      desc 'Get the diff for a specific commit of a project' do
        failure [[404, 'Not Found']]
      end
      params do
        requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
      end
97
      get ":id/repository/commits/:sha/diff" do
98 99
        commit = user_project.commit(params[:sha])

100
        not_found! "Commit" unless commit
101

102
        commit.raw_diffs.to_a
103
      end
104

105 106 107 108 109 110 111 112 113
      desc "Get a commit's comments" do
        success Entities::CommitNote
        failure [[404, 'Not Found']]
      end
      params do
        requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
        optional :per_page, type: Integer, desc: 'The amount of items per page for paginaion'
        optional :page, type: Integer, desc: 'The page number for pagination'
      end
114
      get ':id/repository/commits/:sha/comments' do
115 116
        commit = user_project.commit(params[:sha])

117
        not_found! 'Commit' unless commit
118
        notes = Note.where(commit_id: commit.id).order(:created_at)
119

120 121 122
        present paginate(notes), with: Entities::CommitNote
      end

123 124 125 126 127 128 129 130 131 132 133 134
      desc 'Post comment to commit' do
        success Entities::CommitNote
      end
      params do
        requires :sha, type: String, regexp: /\A\h{6,40}\z/, desc: "The commit's SHA"
        requires :note, type: String, desc: 'The text of the comment'
        optional :path, type: String, desc: 'The file path'
        given :path do
          requires :line, type: Integer, desc: 'The line number'
          requires :line_type, type: String, values: ['new', 'old'], default: 'new', desc: 'The type of the line'
        end
      end
135
      post ':id/repository/commits/:sha/comments' do
136
        commit = user_project.commit(params[:sha])
137
        not_found! 'Commit' unless commit
138

139 140 141 142 143 144
        opts = {
          note: params[:note],
          noteable_type: 'Commit',
          commit_id: commit.id
        }

145
        if params[:path]
146
          commit.raw_diffs(all_diffs: true).each do |diff|
147
            next unless diff.new_path == params[:path]
148
            lines = Gitlab::Diff::Parser.new.parse(diff.diff.each_line)
149 150

            lines.each do |line|
151
              next unless line.new_pos == params[:line] && line.type == params[:line_type]
152 153 154 155 156
              break opts[:line_code] = Gitlab::Diff::LineCode.generate(diff.new_path, line.new_pos, line.old_pos)
            end

            break if opts[:line_code]
          end
157 158

          opts[:type] = LegacyDiffNote.name if opts[:line_code]
159 160 161 162 163 164 165
        end

        note = ::Notes::CreateService.new(user_project, current_user, opts).execute

        if note.save
          present note, with: Entities::CommitNote
        else
166
          render_api_error!("Failed to save note #{note.errors.messages}", 400)
167 168
        end
      end
169 170 171
    end
  end
end