Move check from MergeRequests::BuildService to MergeRequest model validation
Everyone can contribute. Help move this issue forward while earning points, leveling up and collecting rewards.
What it is
MergeRequests::BuildService is used to determine if we're going to show the preview of a new merge request, pre-filling some data in the form and ask the user to enter the other information in order to create a merge request.
What to do
Following the conversation from https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9263#note_23507289, I propose that we:
- Remove
MergeRequest#can_be_created - Move all the checks in
MergeRequests::BuildServiceto be validations onMergeRequest - Replace the check to
MergeRequest#can_be_createdto beMergeRequest#valid? - Make
MergeRequests::BuildServiceonly pre-fill data and handle error messages if extra action needed.
The validation could look something like:
# Note that the below check should happen after `validate_branches` and so on
validate :validate_commit_existence, on: :create
def validate_commit_existence
unless source_project.commit(source_branch)
errors.add(:source_commit, '...')
end
unless target_project.commit(target_branch)
errors.add(:target_commit, '...')
end
end
And update the error messages accordingly.
Rationale behind this
MergeRequest#can_be_created is really a bad way to pass data. The way it is right now is like saving a local variable in another object, while the object saving the data has no idea what it is. Ideally, validations should not be a state in the record, but the validation itself. Something like:
valid = Validate::MergeRequest.new(merge_request).validate
Would work much better because if we're ever going to validate twice (or use a different validation because, see MergeRequest#allow_broken), we just need to create a new validation object instead of clearing the state on the record. This works better:
valid = Validate::MergeRequest.new(merge_request).validate
merge_request.update(params)
valid_again = Validate::MergeRequest.new(merge_request).validate
Than this:
valid = merge_request.valid?
merge_request.update(params)
merge_request.reset_validation # BOO
valid_again = merge_request.valid?
However we're just stuck with Rails, and in this particular case, the object, that is the merge request should totally know if the branches are valid or not, which is the only check used for MergeRequest#can_be_created.
Checking the other validations used in MergeRequest:
-
validates :source_project, presence: trueOf course we also want this when previewing -
validates :source_branch, presence: trueSame -
validates :target_project, presence: trueSame -
validates :target_branch, presence: trueSame -
validate :validate_branchesWhy are we doing similar thing inMergeRequests::BuildServicetoo? This is where we should merge the checks. -
validate :validate_forkThis won't hurt
That means we certainly also want the above to pass even if we're previewing the merge request.
All the other attr_accessor should probably also be removed from MergeRequest, but each one would have their own story and would be another topic.
p.s. MergeRequests::BuildService is really in a very confusing state, probably only better than CreatesCommit