summaryrefslogtreecommitdiff
path: root/app/services/projects/create_service.rb
blob: 6dc3d8c2416012723c49758d4c53ea09e31765f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
module Projects
  class CreateService < BaseService
    def initialize(user, params)
      @current_user, @params = user, params.dup
    end

    def execute
      forked_from_project_id = params.delete(:forked_from_project_id)
      import_data = params.delete(:import_data)
      @skip_wiki = params.delete(:skip_wiki)

      @project = Project.new(params)

      # Make sure that the user is allowed to use the specified visibility level
      unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level])
        deny_visibility_level(@project)
        return @project
      end

      unless allowed_fork?(forked_from_project_id)
        @project.errors.add(:forked_from_project_id, 'is forbidden')
        return @project
      end

      set_project_name_from_path

      # get namespace id
      namespace_id = params[:namespace_id]

      if namespace_id
        # Find matching namespace and check if it allowed
        # for current user if namespace_id passed.
        unless allowed_namespace?(current_user, namespace_id)
          @project.namespace_id = nil
          deny_namespace
          return @project
        end
      else
        # Set current user namespace if namespace_id is nil
        @project.namespace_id = current_user.namespace_id
      end

      @project.creator = current_user

      if forked_from_project_id
        @project.build_forked_project_link(forked_from_project_id: forked_from_project_id)
      end

      save_project_and_import_data(import_data)

      @project.import_start if @project.import?

      after_create_actions if @project.persisted?

      if @project.errors.empty?
        @project.add_import_job if @project.import?
      else
        fail(error: @project.errors.full_messages.join(', '))
      end
      @project
    rescue => e
      fail(error: e.message)
    end

    protected

    def deny_namespace
      @project.errors.add(:namespace, "is not valid")
    end

    def allowed_fork?(source_project_id)
      return true if source_project_id.nil?

      source_project = Project.find_by(id: source_project_id)
      current_user.can?(:fork_project, source_project)
    end

    def allowed_namespace?(user, namespace_id)
      namespace = Namespace.find_by(id: namespace_id)
      current_user.can?(:create_projects, namespace)
    end

    def after_create_actions
      log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"")

      unless @project.gitlab_project_import?
        @project.create_wiki unless skip_wiki?
        create_services_from_active_templates(@project)

        @project.create_labels
      end

      event_service.create_project(@project, current_user)
      system_hook_service.execute_hooks_for(@project, :create)

      unless @project.group || @project.gitlab_project_import?
        @project.team << [current_user, :master, current_user]
      end

      @project.group&.refresh_members_authorized_projects
    end

    def skip_wiki?
      !@project.feature_available?(:wiki, current_user) || @skip_wiki
    end

    def save_project_and_import_data(import_data)
      Project.transaction do
        @project.create_or_update_import_data(data: import_data[:data], credentials: import_data[:credentials]) if import_data

        if @project.save && !@project.import?
          raise 'Failed to create repository' unless @project.create_repository
        end
      end
    end

    def fail(error:)
      message = "Unable to save project. Error: #{error}"
      message << "Project ID: #{@project.id}" if @project && @project.id

      Rails.logger.error(message)

      if @project && @project.import?
        @project.errors.add(:base, message)
        @project.mark_import_as_failed(message)
      end

      @project
    end

    def create_services_from_active_templates(project)
      Service.where(template: true, active: true).each do |template|
        service = Service.build_from_template(project.id, template)
        service.save!
      end
    end

    def set_project_name_from_path
      # Set project name from path
      if @project.name.present? && @project.path.present?
        # if both name and path set - everything is ok
      elsif @project.path.present?
        # Set project name from path
        @project.name = @project.path.dup
      elsif @project.name.present?
        # For compatibility - set path from name
        # TODO: remove this in 8.0
        @project.path = @project.name.dup.parameterize
      end
    end
  end
end