debian-mirror-gitlab/app/services/packages/npm/create_package_service.rb

108 lines
3.2 KiB
Ruby
Raw Normal View History

2020-07-28 23:09:34 +05:30
# frozen_string_literal: true
module Packages
module Npm
2020-11-24 15:15:51 +05:30
class CreatePackageService < ::Packages::CreatePackageService
2020-07-28 23:09:34 +05:30
include Gitlab::Utils::StrongMemoize
2021-12-11 22:18:48 +05:30
PACKAGE_JSON_NOT_ALLOWED_FIELDS = %w[readme readmeFilename].freeze
2020-07-28 23:09:34 +05:30
def execute
return error('Version is empty.', 400) if version.blank?
return error('Package already exists.', 403) if current_package_exists?
2020-11-24 15:15:51 +05:30
return error('File is too large.', 400) if file_size_exceeded?
2020-07-28 23:09:34 +05:30
2021-10-27 15:23:28 +05:30
ApplicationRecord.transaction { create_npm_package! }
2020-07-28 23:09:34 +05:30
end
private
2020-11-24 15:15:51 +05:30
def create_npm_package!
package = create_package!(:npm, name: name, version: version)
2020-07-28 23:09:34 +05:30
::Packages::CreatePackageFileService.new(package, file_params).execute
::Packages::CreateDependencyService.new(package, package_dependencies).execute
::Packages::Npm::CreateTagService.new(package, dist_tag).execute
2022-01-26 12:08:38 +05:30
package.create_npm_metadatum!(package_json: package_json)
2021-12-11 22:18:48 +05:30
2020-07-28 23:09:34 +05:30
package
end
def current_package_exists?
project.packages
.npm
.with_name(name)
.with_version(version)
.exists?
end
def name
params[:name]
end
def version
strong_memoize(:version) do
params[:versions].each_key.first
end
end
def version_data
params[:versions][version]
end
2021-12-11 22:18:48 +05:30
def package_json
version_data.except(*PACKAGE_JSON_NOT_ALLOWED_FIELDS)
end
2020-07-28 23:09:34 +05:30
def dist_tag
params['dist-tags'].each_key.first
end
def package_file_name
strong_memoize(:package_file_name) do
"#{name}-#{version}.tgz"
end
end
def attachment
strong_memoize(:attachment) do
params['_attachments'][package_file_name]
end
end
2022-01-12 12:59:36 +05:30
# TODO (technical debt): Extract the package size calculation to its own component and unit test it separately.
def calculated_package_file_size
strong_memoize(:calculated_package_file_size) do
# This calculation is based on:
# 1. 4 chars in a Base64 encoded string are 3 bytes in the original string. Meaning 1 char is 0.75 bytes.
# 2. The encoded string may have 1 or 2 extra '=' chars used for padding. Each padding char means 1 byte less in the original string.
# Reference:
# - https://blog.aaronlenoir.com/2017/11/10/get-original-length-from-base-64-string/
# - https://en.wikipedia.org/wiki/Base64#Decoding_Base64_with_padding
encoded_data = attachment['data']
((encoded_data.length * 0.75 ) - encoded_data[-2..].count('=')).to_i
end
end
2020-07-28 23:09:34 +05:30
def file_params
{
file: CarrierWaveStringFile.new(Base64.decode64(attachment['data'])),
2022-01-12 12:59:36 +05:30
size: calculated_package_file_size,
2020-07-28 23:09:34 +05:30
file_sha1: version_data[:dist][:shasum],
2021-01-29 00:20:46 +05:30
file_name: package_file_name,
build: params[:build]
2020-07-28 23:09:34 +05:30
}
end
def package_dependencies
_version, versions_data = params[:versions].first
versions_data
end
2020-11-24 15:15:51 +05:30
def file_size_exceeded?
2022-01-12 12:59:36 +05:30
project.actual_limits.exceeded?(:npm_max_file_size, calculated_package_file_size)
2020-11-24 15:15:51 +05:30
end
2020-07-28 23:09:34 +05:30
end
end
end