summaryrefslogtreecommitdiff
path: root/lib/gitlab/database/rename_reserved_paths_migration/rename_base.rb
blob: 4d454fd8ea07f805fe9d50b1da88912a7fd49e70 (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
module Gitlab
  module Database
    module RenameReservedPathsMigration
      class RenameBase
        attr_reader :paths, :migration

        delegate :update_column_in_batches,
                 :replace_sql,
                 to: :migration

        def initialize(paths, migration)
          @paths = paths
          @migration = migration
        end

        def path_patterns
          @path_patterns ||= paths.map { |path| "%#{path}" }
        end

        def rename_path_for_routable(routable)
          old_path = routable.path
          old_full_path = routable.full_path
          # Only remove the last occurrence of the path name to get the parent namespace path
          namespace_path = remove_last_occurrence(old_full_path, old_path)
          new_path = rename_path(namespace_path, old_path)
          new_full_path = join_routable_path(namespace_path, new_path)

          # skips callbacks & validations
          routable.class.where(id: routable).
            update_all(path: new_path)

          rename_routes(old_full_path, new_full_path)

          [old_full_path, new_full_path]
        end

        def rename_routes(old_full_path, new_full_path)
          replace_statement = replace_sql(Route.arel_table[:path],
                                          old_full_path,
                                          new_full_path)

          update_column_in_batches(:routes, :path, replace_statement)  do |table, query|
            query.where(MigrationClasses::Route.arel_table[:path].matches("#{old_full_path}%"))
          end
        end

        def rename_path(namespace_path, path_was)
          counter = 0
          path = "#{path_was}#{counter}"

          while route_exists?(join_routable_path(namespace_path, path))
            counter += 1
            path = "#{path_was}#{counter}"
          end

          path
        end

        def remove_last_occurrence(string, pattern)
          string.reverse.sub(pattern.reverse, "").reverse
        end

        def join_routable_path(namespace_path, top_level)
          if namespace_path.present?
            File.join(namespace_path, top_level)
          else
            top_level
          end
        end

        def route_exists?(full_path)
          MigrationClasses::Route.where(Route.arel_table[:path].matches(full_path)).any?
        end

        def move_pages(old_path, new_path)
          move_folders(pages_dir, old_path, new_path)
        end

        def move_uploads(old_path, new_path)
          return unless file_storage?

          move_folders(uploads_dir, old_path, new_path)
        end

        def move_folders(directory, old_relative_path, new_relative_path)
          old_path = File.join(directory, old_relative_path)
          return unless File.directory?(old_path)

          new_path = File.join(directory, new_relative_path)
          FileUtils.mv(old_path, new_path)
        end

        def file_storage?
          CarrierWave::Uploader::Base.storage == CarrierWave::Storage::File
        end

        def uploads_dir
          File.join(CarrierWave.root, "uploads")
        end

        def pages_dir
          Settings.pages.path
        end
      end
    end
  end
end