Welcome to mirror list, hosted at ThFree Co, Russian Federation.

generate_metadata_service.rb « npm « packages « services « app - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: c84c170b2341ed2fc69c8688fe6f8cb5042a34bd (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
# frozen_string_literal: true

module Packages
  module Npm
    class GenerateMetadataService
      include API::Helpers::RelatedResourcesHelpers

      # Allowed fields are those defined in the abbreviated form
      # defined here: https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-version-object
      # except: name, version, dist, dependencies and xDependencies. Those are generated by this service.
      PACKAGE_JSON_ALLOWED_FIELDS = %w[deprecated bin directories dist engines _hasShrinkwrap].freeze

      def initialize(name, packages)
        @name = name
        @packages = packages
        @dependencies_cache = {}
      end

      def execute(only_dist_tags: false)
        ServiceResponse.success(payload: metadata(only_dist_tags))
      end

      private

      attr_reader :name, :packages, :dependencies_cache

      def metadata(only_dist_tags)
        result = { dist_tags: dist_tags }

        unless only_dist_tags
          result[:name] = name
          result[:versions] = versions
        end

        result
      end

      def versions
        package_versions = {}

        packages.each_batch do |relation|
          batched_packages = if Feature.enabled?(:npm_optimize_metadata_generation)
                               fill_dependencies_cache(relation)

                               relation.including_dependency_links
                                       .preload_files
                                       .preload_npm_metadatum
                             else
                               relation.including_dependency_links_with_dependencies
                                       .preload_files
                                       .preload_npm_metadatum
                             end

          batched_packages.each do |package|
            package_file = package.installable_package_files.last

            next unless package_file

            package_versions[package.version] = build_package_version(package, package_file)
          end
        end

        package_versions
      end

      def dist_tags
        build_package_tags.tap { |t| t['latest'] ||= sorted_versions.last }
      end

      def build_package_tags
        package_tags.to_h { |tag| [tag.name, tag.package.version] }
      end

      def build_package_version(package, package_file)
        abbreviated_package_json(package).merge(
          name: package.name,
          version: package.version,
          dist: {
            shasum: package_file.file_sha1,
            tarball: tarball_url(package, package_file)
          }
        ).tap do |package_version|
          package_version.merge!(build_package_dependencies(package))
        end
      end

      def tarball_url(package, package_file)
        expose_url api_v4_projects_packages_npm_package_name___file_name_path(
          { id: package.project_id, package_name: package.name, file_name: package_file.file_name }, true
        )
      end

      def build_package_dependencies(package)
        dependencies = Hash.new { |h, key| h[key] = {} }

        package.dependency_links.each do |dependency_link|
          dependency = if Feature.enabled?(:npm_optimize_metadata_generation)
                         dependencies_cache[dependency_link.dependency_id]
                       else
                         dependency_link.dependency
                       end

          dependencies[dependency_link.dependency_type][dependency.name] = dependency.version_pattern
        end

        dependencies
      end

      def sorted_versions
        versions = packages.pluck_versions.compact
        VersionSorter.sort(versions)
      end

      def package_tags
        Packages::Tag.for_package_ids(packages)
                     .preload_package
      end

      def abbreviated_package_json(package)
        json = package.npm_metadatum&.package_json || {}
        json.slice(*PACKAGE_JSON_ALLOWED_FIELDS)
      end

      def fill_dependencies_cache(packages)
        Packages::Dependency
          .with_packages(packages)
          .id_not_in(dependencies_cache.keys)
          .each_batch do |dependencies|
            dependencies.each do |dependency|
              dependencies_cache[dependency.id] = dependency
            end
          end
      end
    end
  end
end