From 6e5e14fc1bdf3f0378311445f8665fc066218733 Mon Sep 17 00:00:00 2001 From: Dzmitry Meshcharakou <12459192-dmeshcharakou@users.noreply.gitlab.com> Date: Thu, 14 Nov 2024 15:06:39 +0100 Subject: [PATCH 1/7] Add status deprecated to the packages statuses Changelog: added --- app/models/packages/package.rb | 6 +- .../packages/npm/deprecate_package_service.rb | 15 ++++- ...ate_status_for_deprecated_npm_packages.yml | 8 +++ ...eprecate_exist_to_packages_npm_metadata.rb | 16 +++++ ...date_status_for_deprecated_npm_packages.rb | 27 +++++++++ db/schema_migrations/20241129124907 | 1 + db/schema_migrations/20241201164238 | 1 + db/structure.sql | 2 + doc/api/graphql/reference/index.md | 1 + ...date_status_for_deprecated_npm_packages.rb | 25 ++++++++ .../packages/package_status_enum_spec.rb | 4 +- ...status_for_deprecated_npm_packages_spec.rb | 60 +++++++++++++++++++ ...status_for_deprecated_npm_packages_spec.rb | 27 +++++++++ spec/models/packages/package_spec.rb | 2 + .../npm/deprecate_package_service_spec.rb | 47 +++++++++++---- .../packages/installable_shared_examples.rb | 2 + 16 files changed, 229 insertions(+), 15 deletions(-) create mode 100644 db/docs/batched_background_migrations/update_status_for_deprecated_npm_packages.yml create mode 100644 db/post_migrate/20241129124907_add_index_on_package_json_deprecate_exist_to_packages_npm_metadata.rb create mode 100644 db/post_migrate/20241201164238_queue_update_status_for_deprecated_npm_packages.rb create mode 100644 db/schema_migrations/20241129124907 create mode 100644 db/schema_migrations/20241201164238 create mode 100644 lib/gitlab/background_migration/update_status_for_deprecated_npm_packages.rb create mode 100644 spec/lib/gitlab/background_migration/update_status_for_deprecated_npm_packages_spec.rb create mode 100644 spec/migrations/20241201164238_queue_update_status_for_deprecated_npm_packages_spec.rb diff --git a/app/models/packages/package.rb b/app/models/packages/package.rb index 003e96c7910518..621ecd7600b912 100644 --- a/app/models/packages/package.rb +++ b/app/models/packages/package.rb @@ -9,8 +9,8 @@ class Packages::Package < ApplicationRecord include Packages::Downloadable include EnumInheritance - DISPLAYABLE_STATUSES = [:default, :error].freeze - INSTALLABLE_STATUSES = [:default, :hidden].freeze + DISPLAYABLE_STATUSES = [:default, :error, :deprecated].freeze + INSTALLABLE_STATUSES = [:default, :hidden, :deprecated].freeze STATUS_MESSAGE_MAX_LENGTH = 255 enum package_type: { @@ -30,7 +30,7 @@ class Packages::Package < ApplicationRecord ml_model: 14 } - enum status: { default: 0, hidden: 1, processing: 2, error: 3, pending_destruction: 4 } + enum status: { default: 0, hidden: 1, processing: 2, error: 3, pending_destruction: 4, deprecated: 5 } belongs_to :project belongs_to :creator, class_name: 'User' diff --git a/app/services/packages/npm/deprecate_package_service.rb b/app/services/packages/npm/deprecate_package_service.rb index fc4a670b5f56fb..aa7ff146ef869e 100644 --- a/app/services/packages/npm/deprecate_package_service.rb +++ b/app/services/packages/npm/deprecate_package_service.rb @@ -18,7 +18,13 @@ def execute attributes = relation.preload_npm_metadatum.filter_map { |package| metadatum_attributes(package) } next if attributes.empty? - ::Packages::Npm::Metadatum.upsert_all(attributes) + package_ids = attributes.pluck(:package_id) # rubocop:disable Database/AvoidUsingPluckWithoutLimit, CodeReuse/ActiveRecord -- Pluck used on Hashes + + ApplicationRecord.transaction do + ::Packages::Npm::Metadatum.upsert_all(attributes) + ::Packages::Package.id_in(package_ids).update_all(status: package_status) + end + enqueue_metadata_cache_worker = true end @@ -85,6 +91,13 @@ def deprecation_message metadatum['deprecated'] end strong_memoize_attr :deprecation_message + + def package_status + return :default if deprecation_message_empty? + + :deprecated + end + strong_memoize_attr :package_status end end end diff --git a/db/docs/batched_background_migrations/update_status_for_deprecated_npm_packages.yml b/db/docs/batched_background_migrations/update_status_for_deprecated_npm_packages.yml new file mode 100644 index 00000000000000..4194c8608e0ef7 --- /dev/null +++ b/db/docs/batched_background_migrations/update_status_for_deprecated_npm_packages.yml @@ -0,0 +1,8 @@ +--- +migration_job_name: UpdateStatusForDeprecatedNpmPackages +description: Update status to deprecated for deprecated npm packages +feature_category: package_registry +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/174312 +milestone: '17.7' +queued_migration_version: 20241201164238 +finalized_by: # version of the migration that finalized this BBM diff --git a/db/post_migrate/20241129124907_add_index_on_package_json_deprecate_exist_to_packages_npm_metadata.rb b/db/post_migrate/20241129124907_add_index_on_package_json_deprecate_exist_to_packages_npm_metadata.rb new file mode 100644 index 00000000000000..9c1d0007205e88 --- /dev/null +++ b/db/post_migrate/20241129124907_add_index_on_package_json_deprecate_exist_to_packages_npm_metadata.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class AddIndexOnPackageJsonDeprecateExistToPackagesNpmMetadata < Gitlab::Database::Migration[2.2] + disable_ddl_transaction! + milestone '17.7' + + INDEX_NAME = 'index_packages_npm_metadata_on_package_json_deprecate_exist' + + def up + add_concurrent_index :packages_npm_metadata, :package_id, where: "(package_json ? 'deprecated')", name: INDEX_NAME + end + + def down + remove_concurrent_index_by_name :packages_npm_metadata, INDEX_NAME + end +end diff --git a/db/post_migrate/20241201164238_queue_update_status_for_deprecated_npm_packages.rb b/db/post_migrate/20241201164238_queue_update_status_for_deprecated_npm_packages.rb new file mode 100644 index 00000000000000..67d59db519ef71 --- /dev/null +++ b/db/post_migrate/20241201164238_queue_update_status_for_deprecated_npm_packages.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class QueueUpdateStatusForDeprecatedNpmPackages < Gitlab::Database::Migration[2.2] + milestone '17.7' + + restrict_gitlab_migration gitlab_schema: :gitlab_main + + MIGRATION = 'UpdateStatusForDeprecatedNpmPackages' + DELAY_INTERVAL = 2.minutes + BATCH_SIZE = 200 + SUB_BATCH_SIZE = 100 + + def up + queue_batched_background_migration( + MIGRATION, + :packages_npm_metadata, + :package_id, + job_interval: DELAY_INTERVAL, + batch_size: BATCH_SIZE, + sub_batch_size: SUB_BATCH_SIZE + ) + end + + def down + delete_batched_background_migration(MIGRATION, :packages_npm_metadata, :package_id, []) + end +end diff --git a/db/schema_migrations/20241129124907 b/db/schema_migrations/20241129124907 new file mode 100644 index 00000000000000..ea1bae157d6e87 --- /dev/null +++ b/db/schema_migrations/20241129124907 @@ -0,0 +1 @@ +7ebd2deb433fdb61697742d2a122dc356d9c0ba4a4eb219582c9a139ae8dc9b2 \ No newline at end of file diff --git a/db/schema_migrations/20241201164238 b/db/schema_migrations/20241201164238 new file mode 100644 index 00000000000000..0852e7d2fbadf3 --- /dev/null +++ b/db/schema_migrations/20241201164238 @@ -0,0 +1 @@ +9e4719cd1baf7a689ce931d864d887a30b3a02ca61d15ea88ac05f7b890f1023 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 04ea6be00054da..9b6e01209fbc5a 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -32009,6 +32009,8 @@ CREATE UNIQUE INDEX index_packages_npm_metadata_caches_on_object_storage_key ON CREATE INDEX index_packages_npm_metadata_caches_on_project_id_status ON packages_npm_metadata_caches USING btree (project_id, status); +CREATE INDEX index_packages_npm_metadata_on_package_json_deprecate_exist ON packages_npm_metadata USING btree (package_id) WHERE (package_json ? 'deprecated'::text); + CREATE INDEX index_packages_npm_metadata_on_project_id ON packages_npm_metadata USING btree (project_id); CREATE INDEX index_packages_nuget_dl_metadata_on_dependency_link_id ON packages_nuget_dependency_link_metadata USING btree (dependency_link_id); diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 2e3c3c0e5167cf..cf4a96677d0c5a 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -40389,6 +40389,7 @@ Values for sorting package. | Value | Description | | ----- | ----------- | | <a id="packagestatusdefault"></a>`DEFAULT` | Packages with a default status. | +| <a id="packagestatusdeprecated"></a>`DEPRECATED` | Packages with a deprecated status. | | <a id="packagestatuserror"></a>`ERROR` | Packages with a error status. | | <a id="packagestatushidden"></a>`HIDDEN` | Packages with a hidden status. | | <a id="packagestatuspending_destruction"></a>`PENDING_DESTRUCTION` | Packages with a pending_destruction status. | diff --git a/lib/gitlab/background_migration/update_status_for_deprecated_npm_packages.rb b/lib/gitlab/background_migration/update_status_for_deprecated_npm_packages.rb new file mode 100644 index 00000000000000..3d599b706257e2 --- /dev/null +++ b/lib/gitlab/background_migration/update_status_for_deprecated_npm_packages.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Gitlab + module BackgroundMigration + class UpdateStatusForDeprecatedNpmPackages < BatchedMigrationJob + operation_name :update_all + scope_to ->(relation) { relation.where("package_json ? 'deprecated'") } + feature_category :package_registry + + STATUS_DEPRECATED = 5 + + module Packages + class Package < ApplicationRecord + self.table_name = 'packages_packages' + end + end + + def perform + each_sub_batch do |sub_batch| + Packages::Package.id_in(sub_batch.select(:package_id)).update_all(status: STATUS_DEPRECATED) + end + end + end + end +end diff --git a/spec/graphql/types/packages/package_status_enum_spec.rb b/spec/graphql/types/packages/package_status_enum_spec.rb index 2c79c92498bbea..4eaf7fa3b154c7 100644 --- a/spec/graphql/types/packages/package_status_enum_spec.rb +++ b/spec/graphql/types/packages/package_status_enum_spec.rb @@ -4,6 +4,8 @@ RSpec.describe GitlabSchema.types['PackageStatus'] do it 'exposes all package statuses' do - expect(described_class.values.keys).to contain_exactly(*%w[DEFAULT HIDDEN PROCESSING ERROR PENDING_DESTRUCTION]) + expect(described_class.values.keys).to contain_exactly( + *%w[DEFAULT DEPRECATED HIDDEN PROCESSING ERROR PENDING_DESTRUCTION] + ) end end diff --git a/spec/lib/gitlab/background_migration/update_status_for_deprecated_npm_packages_spec.rb b/spec/lib/gitlab/background_migration/update_status_for_deprecated_npm_packages_spec.rb new file mode 100644 index 00000000000000..687c8a6ea18dbc --- /dev/null +++ b/spec/lib/gitlab/background_migration/update_status_for_deprecated_npm_packages_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::BackgroundMigration::UpdateStatusForDeprecatedNpmPackages, feature_category: :package_registry do + let!(:organization) { table(:organizations).create!(name: 'organization', path: 'organization') } + + let!(:namespace) { table(:namespaces).create!(name: 'group', path: 'group', type: 'Group') } + + let!(:project) do + table(:projects).create!(name: 'project', path: 'project', project_namespace_id: namespace.id, + namespace_id: namespace.id, organization_id: organization.id) + end + + let!(:package_1) do + table(:packages_packages).create!(name: 'test', version: '1.0.0', package_type: 2, project_id: project.id) + end + + let!(:package_2) do + table(:packages_packages).create!(name: 'test', version: '2.0.0', package_type: 2, project_id: project.id) + end + + let!(:package_3) do + table(:packages_packages).create!(name: 'test', version: '3.0.0', package_type: 2, project_id: project.id) + end + + let!(:metadata_1) do + table(:packages_npm_metadata).create!(package_json: { deprecated: 'not supported' }, package_id: package_1.id) + end + + let!(:metadata_2) do + table(:packages_npm_metadata).create!(package_json: { deprecated: 'not supported' }, package_id: package_2.id) + end + + let!(:metadata_3) do + table(:packages_npm_metadata).create!(package_json: {}, package_id: package_3.id) + end + + let!(:starting_id) { table(:packages_npm_metadata).minimum(:package_id) } + let!(:end_id) { table(:packages_npm_metadata).maximum(:package_id) } + + let!(:migration) do + described_class.new( + start_id: starting_id, + end_id: end_id, + batch_table: :packages_npm_metadata, + batch_column: :package_id, + sub_batch_size: 2, + pause_ms: 0, + connection: ::ApplicationRecord.connection + ) + end + + it 'updates the status for deprecated npm packages' do + expect { migration.perform } + .to change { package_1.reload.status }.from(0).to(5) + .and change { package_2.reload.status }.from(0).to(5) + .and not_change { package_3.reload.status } + end +end diff --git a/spec/migrations/20241201164238_queue_update_status_for_deprecated_npm_packages_spec.rb b/spec/migrations/20241201164238_queue_update_status_for_deprecated_npm_packages_spec.rb new file mode 100644 index 00000000000000..deedf24b63f278 --- /dev/null +++ b/spec/migrations/20241201164238_queue_update_status_for_deprecated_npm_packages_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe QueueUpdateStatusForDeprecatedNpmPackages, migration: :gitlab_main, feature_category: :package_registry do + let!(:batched_migration) { described_class::MIGRATION } + + it 'schedules a new batched migration' do + reversible_migration do |migration| + migration.before -> { + expect(batched_migration).not_to have_scheduled_batched_migration + } + + migration.after -> { + expect(batched_migration).to have_scheduled_batched_migration( + gitlab_schema: :gitlab_main, + table_name: :packages_npm_metadata, + column_name: :package_id, + interval: described_class::DELAY_INTERVAL, + batch_size: described_class::BATCH_SIZE, + sub_batch_size: described_class::SUB_BATCH_SIZE + ) + } + end + end +end diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb index 5fde93519c052a..4636186d881679 100644 --- a/spec/models/packages/package_spec.rb +++ b/spec/models/packages/package_spec.rb @@ -344,12 +344,14 @@ let_it_be(:hidden_package) { create(:maven_package, :hidden) } let_it_be(:processing_package) { create(:maven_package, :processing) } let_it_be(:error_package) { create(:maven_package, :error) } + let_it_be(:deprecated_package) { create(:maven_package, :deprecated) } describe '.displayable' do subject { described_class.displayable } it 'does not include non-displayable packages', :aggregate_failures do is_expected.to include(error_package) + is_expected.to include(deprecated_package) is_expected.not_to include(hidden_package) is_expected.not_to include(processing_package) end diff --git a/spec/services/packages/npm/deprecate_package_service_spec.rb b/spec/services/packages/npm/deprecate_package_service_spec.rb index 65f8cae26d9fb4..97b273d7f70cc2 100644 --- a/spec/services/packages/npm/deprecate_package_service_spec.rb +++ b/spec/services/packages/npm/deprecate_package_service_spec.rb @@ -44,15 +44,20 @@ package_json.merge('deprecated' => 'old deprecation message')) end - it 'adds or updates the deprecated field' do + it 'adds or updates the deprecated field and sets status to `deprecated`' do expect { execute } - .to change { package_1.reload.npm_metadatum.package_json['deprecated'] }.to('This version is deprecated') - .and change { package_2.reload.npm_metadatum.package_json['deprecated'] } - .from('old deprecation message').to('This version is deprecated') + .to change { + package_1.reload.npm_metadatum.package_json['deprecated'] + }.to('This version is deprecated') + .and change { + package_2.reload.npm_metadatum.package_json['deprecated'] + }.from('old deprecation message').to('This version is deprecated') + .and change { package_1.status }.from('default').to('deprecated') + .and change { package_2.status }.from('default').to('deprecated') expect(execute).to be_success end - it 'executes 5 queries' do + it 'executes 8 queries' do queries = ActiveRecord::QueryRecorder.new do execute end @@ -61,11 +66,30 @@ # 2. each_batch upper bound # 3. SELECT packages_packages.id, packages_packages.version FROM packages_packages # 4. SELECT packages_npm_metadata.* FROM packages_npm_metadata - # 5. UPDATE packages_npm_metadata SET package_json = - expect(queries.count).to eq(5) + # 5. TRANSACTION + # 6. INSERT INSERT INTO packages_npm_metadata (...) VALUES (...) DO UPDATE SET ... + # 7. UPDATE packages_packages SET status = + # 8. TRANSACTION + expect(queries.count).to eq(8) end it_behaves_like 'enqueues metadata cache worker' + + context 'when the database error has happened' do + let(:exception) { ActiveRecord::ConnectionTimeoutError } + + it 'rolls back previously successfully executed operations' do + allow(::Packages::Package).to receive(:id_in).and_raise(exception) + + expect { execute }.to raise_error(exception) + .and not_change { + package_1.reload.npm_metadatum.package_json['deprecated'] + }.from(nil) + .and not_change { + package_2.reload.npm_metadatum.package_json['deprecated'] + }.from('old deprecation message') + end + end end context 'when passing deprecated as empty string' do @@ -74,12 +98,15 @@ before do package_json = package_1.npm_metadatum.package_json package_1.npm_metadatum.update!(package_json: package_json.merge('deprecated' => 'This version is deprecated')) + package_1.update!(status: :deprecated) end - it 'removes the deprecation warning', :aggregate_failures do + it 'removes the deprecation warning and sets status to `default`', :aggregate_failures do expect { execute } - .to change { package_1.reload.npm_metadatum.package_json['deprecated'] } - .from('This version is deprecated').to(nil) + .to change { + package_1.reload.npm_metadatum.package_json['deprecated'] + }.from('This version is deprecated').to(nil) + .and change { package_1.status }.from('deprecated').to('default') expect(execute).to be_success end diff --git a/spec/support/shared_examples/packages/installable_shared_examples.rb b/spec/support/shared_examples/packages/installable_shared_examples.rb index 9a4e52ff6f76dc..74f3e695a01d8c 100644 --- a/spec/support/shared_examples/packages/installable_shared_examples.rb +++ b/spec/support/shared_examples/packages/installable_shared_examples.rb @@ -6,6 +6,7 @@ let_it_be(:hidden_package) { create(factory_name, :hidden) } let_it_be(:processing_package) { create(factory_name, :processing) } let_it_be(:error_package) { create(factory_name, :error) } + let_it_be(:deprecated_package) { create(factory_name, :deprecated) } subject { described_class.installable } @@ -17,6 +18,7 @@ it 'includes installable packages' do is_expected.to include(default_package) is_expected.to include(hidden_package) + is_expected.to include(deprecated_package) end end end -- GitLab From 4e60edce929e3106a50717afc7cb5d80d6eaaa56 Mon Sep 17 00:00:00 2001 From: Dzmitry Meshcharakou <12459192-dmeshcharakou@users.noreply.gitlab.com> Date: Wed, 11 Dec 2024 20:37:45 +0100 Subject: [PATCH 2/7] Change the API endpoints to also return deprecated packages --- .../resolvers/package_details_resolver.rb | 2 +- .../types/packages/package_links_type.rb | 2 +- app/models/packages/package.rb | 5 +++ .../packages/npm/deprecate_package_service.rb | 6 ++-- doc/api/packages.md | 8 ++--- lib/api/entities/package.rb | 2 +- lib/api/project_packages.rb | 4 +-- .../package_details_resolver_spec.rb | 8 +++++ spec/lib/api/entities/package_spec.rb | 18 +++++++--- ...status_for_deprecated_npm_packages_spec.rb | 21 +++++------ spec/models/packages/package_spec.rb | 19 ++++++++++ .../api/graphql/packages/package_spec.rb | 36 +++++++++++++------ 12 files changed, 93 insertions(+), 38 deletions(-) diff --git a/app/graphql/resolvers/package_details_resolver.rb b/app/graphql/resolvers/package_details_resolver.rb index 8d25d378eb64dd..dc9eecbdd2dfb2 100644 --- a/app/graphql/resolvers/package_details_resolver.rb +++ b/app/graphql/resolvers/package_details_resolver.rb @@ -12,7 +12,7 @@ class PackageDetailsResolver < BaseResolver def resolve(id:) Gitlab::Graphql::Lazy.with_value(find_object(id: id)) do |package| - package if package&.default? + package if package&.detailed_info? end end diff --git a/app/graphql/types/packages/package_links_type.rb b/app/graphql/types/packages/package_links_type.rb index eb29fb655bd691..001391fc256068 100644 --- a/app/graphql/types/packages/package_links_type.rb +++ b/app/graphql/types/packages/package_links_type.rb @@ -12,7 +12,7 @@ class PackageLinksType < BaseObject field :web_path, GraphQL::Types::String, null: true, description: 'Path to the package details page.' def web_path - return unless object.default? + return unless object.detailed_info? package_path(object) end diff --git a/app/models/packages/package.rb b/app/models/packages/package.rb index 621ecd7600b912..1d16dcde8eb536 100644 --- a/app/models/packages/package.rb +++ b/app/models/packages/package.rb @@ -11,6 +11,7 @@ class Packages::Package < ApplicationRecord DISPLAYABLE_STATUSES = [:default, :error, :deprecated].freeze INSTALLABLE_STATUSES = [:default, :hidden, :deprecated].freeze + DETAILED_INFO_STATUSES = [:default, :deprecated].freeze STATUS_MESSAGE_MAX_LENGTH = 255 enum package_type: { @@ -271,6 +272,10 @@ def publish_creation_event ) end + def detailed_info? + DETAILED_INFO_STATUSES.include?(status.to_sym) + end + private # This method will block while another database transaction attempts to insert the same data. diff --git a/app/services/packages/npm/deprecate_package_service.rb b/app/services/packages/npm/deprecate_package_service.rb index aa7ff146ef869e..bd91385dfee9c2 100644 --- a/app/services/packages/npm/deprecate_package_service.rb +++ b/app/services/packages/npm/deprecate_package_service.rb @@ -18,7 +18,7 @@ def execute attributes = relation.preload_npm_metadatum.filter_map { |package| metadatum_attributes(package) } next if attributes.empty? - package_ids = attributes.pluck(:package_id) # rubocop:disable Database/AvoidUsingPluckWithoutLimit, CodeReuse/ActiveRecord -- Pluck used on Hashes + package_ids = attributes.reduce([]) { |memo, attr| memo << attr[:package_id] } ApplicationRecord.transaction do ::Packages::Npm::Metadatum.upsert_all(attributes) @@ -93,9 +93,9 @@ def deprecation_message strong_memoize_attr :deprecation_message def package_status - return :default if deprecation_message_empty? + return ::Packages::Package.statuses[:default] if deprecation_message_empty? - :deprecated + ::Packages::Package.statuses[:deprecated] end strong_memoize_attr :package_status end diff --git a/doc/api/packages.md b/doc/api/packages.md index 944f1991d32b85..7bdfb31472e66c 100644 --- a/doc/api/packages.md +++ b/doc/api/packages.md @@ -20,7 +20,7 @@ The API documentation of [GitLab Packages](../administration/packages/index.md). Get a list of project packages. All package types are included in results. When accessed without authentication, only packages of public projects are returned. -By default, packages with `default` and `error` status are returned. Use the `status` parameter to view other +By default, packages with `default`, `deprecated` and `error` status are returned. Use the `status` parameter to view other packages. ```plaintext @@ -89,7 +89,7 @@ can result in malformed data or broken packages. Get a list of project packages at the group level. When accessed without authentication, only packages of public projects are returned. -By default, packages with `default` and `error` status are returned. Use the `status` parameter to view other +By default, packages with `default`, `deprecated` and `error` status are returned. Use the `status` parameter to view other packages. ```plaintext @@ -187,7 +187,7 @@ can result in malformed data or broken packages. ## Get a project package -Get a single project package. Only packages with status `default` are returned. +Get a single project package. Only packages with status `default` or `deprecated` are returned. ```plaintext GET /projects/:id/packages/:package_id @@ -262,7 +262,7 @@ Example response: The `_links` object contains the following properties: -- `web_path`: The path which you can visit in GitLab and see the details of the package. Only available if the package has status `default`. +- `web_path`: The path which you can visit in GitLab and see the details of the package. Only available if the package has status `default` or `deprecated`. - `delete_api_path`: The API path to delete the package. Only available if the request user has permission to do so. ## List package files diff --git a/lib/api/entities/package.rb b/lib/api/entities/package.rb index a4452daa034e7f..bfde770a83177c 100644 --- a/lib/api/entities/package.rb +++ b/lib/api/entities/package.rb @@ -28,7 +28,7 @@ class Package < Grape::Entity expose :status, documentation: { type: 'string', example: 'default' } expose :_links do - expose :web_path, if: ->(package) { package.default? } do |package| + expose :web_path, if: ->(package) { package.detailed_info? } do |package| package_path(package) end diff --git a/lib/api/project_packages.rb b/lib/api/project_packages.rb index 233171b1279bad..d9a8613069a56e 100644 --- a/lib/api/project_packages.rb +++ b/lib/api/project_packages.rb @@ -77,7 +77,7 @@ def package end route_setting :authentication, job_token_allowed: true get ':id/packages/:package_id' do - render_api_error!('Package not found', 404) unless package.default? + render_api_error!('Package not found', 404) unless package.detailed_info? present package, with: ::API::Entities::Package, user: current_user, namespace: user_project.namespace end @@ -103,7 +103,7 @@ def package end route_setting :authentication, job_token_allowed: true get ':id/packages/:package_id/pipelines' do - not_found!('Package not found') unless package.default? + not_found!('Package not found') unless package.detailed_info? params[:pagination] = 'keyset' # keyset is the only available pagination pipelines = paginate_with_strategies( diff --git a/spec/graphql/resolvers/package_details_resolver_spec.rb b/spec/graphql/resolvers/package_details_resolver_spec.rb index 73ceac39eb29fa..5adb056b9663c1 100644 --- a/spec/graphql/resolvers/package_details_resolver_spec.rb +++ b/spec/graphql/resolvers/package_details_resolver_spec.rb @@ -22,5 +22,13 @@ it { is_expected.to be_nil } end + + context 'when package has status deprecated' do + before do + package.deprecated! + end + + it { is_expected.to eq(package) } + end end end diff --git a/spec/lib/api/entities/package_spec.rb b/spec/lib/api/entities/package_spec.rb index 1b5f77bce16f20..547f1f3be1483f 100644 --- a/spec/lib/api/entities/package_spec.rb +++ b/spec/lib/api/entities/package_spec.rb @@ -7,6 +7,12 @@ subject { described_class.new(package).as_json(namespace: package.project.namespace) } + shared_examples 'expose correct web_path in _links' do + it 'exposes correct web_path in _links' do + expect(subject[:_links][:web_path]).to match('/packages/') + end + end + it 'exposes correct attributes' do expect(subject).to include( :id, @@ -21,9 +27,7 @@ ) end - it 'exposes correct web_path in _links' do - expect(subject[:_links][:web_path]).to match('/packages/') - end + it_behaves_like 'expose correct web_path in _links' context 'with a terraform_module' do let(:package) { create(:terraform_module_package) } @@ -33,7 +37,13 @@ end end - context 'when package has no default status' do + context 'when package has status deprecated' do + let(:package) { create(:package, :deprecated) } + + it_behaves_like 'expose correct web_path in _links' + end + + context 'when package has status error' do let(:package) { create(:package, :error) } it 'does not expose web_path in _links' do diff --git a/spec/lib/gitlab/background_migration/update_status_for_deprecated_npm_packages_spec.rb b/spec/lib/gitlab/background_migration/update_status_for_deprecated_npm_packages_spec.rb index 687c8a6ea18dbc..f426087eadb2bd 100644 --- a/spec/lib/gitlab/background_migration/update_status_for_deprecated_npm_packages_spec.rb +++ b/spec/lib/gitlab/background_migration/update_status_for_deprecated_npm_packages_spec.rb @@ -14,26 +14,23 @@ let!(:package_1) do table(:packages_packages).create!(name: 'test', version: '1.0.0', package_type: 2, project_id: project.id) + .tap do |package| + table(:packages_npm_metadata).create!(package_json: { deprecated: 'not supported' }, package_id: package.id) + end end let!(:package_2) do table(:packages_packages).create!(name: 'test', version: '2.0.0', package_type: 2, project_id: project.id) + .tap do |package| + table(:packages_npm_metadata).create!(package_json: { deprecated: 'not supported' }, package_id: package.id) + end end let!(:package_3) do table(:packages_packages).create!(name: 'test', version: '3.0.0', package_type: 2, project_id: project.id) - end - - let!(:metadata_1) do - table(:packages_npm_metadata).create!(package_json: { deprecated: 'not supported' }, package_id: package_1.id) - end - - let!(:metadata_2) do - table(:packages_npm_metadata).create!(package_json: { deprecated: 'not supported' }, package_id: package_2.id) - end - - let!(:metadata_3) do - table(:packages_npm_metadata).create!(package_json: {}, package_id: package_3.id) + .tap do |package| + table(:packages_npm_metadata).create!(package_json: {}, package_id: package.id) + end end let!(:starting_id) { table(:packages_npm_metadata).minimum(:package_id) } diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb index 4636186d881679..70ebfc8ceb1dbe 100644 --- a/spec/models/packages/package_spec.rb +++ b/spec/models/packages/package_spec.rb @@ -742,4 +742,23 @@ end end end + + describe '#detailed_info?' do + subject { package.detailed_info? } + + where(:status, :result) do + :default | true + :deprecated | true + :hidden | false + :processing | false + :error | false + :pending_destruction | false + end + + with_them do + let(:package) { build(:maven_package, status: status) } + + it { is_expected.to eq(result) } + end + end end diff --git a/spec/requests/api/graphql/packages/package_spec.rb b/spec/requests/api/graphql/packages/package_spec.rb index 99d18fd716097c..ebf9748018785c 100644 --- a/spec/requests/api/graphql/packages/package_spec.rb +++ b/spec/requests/api/graphql/packages/package_spec.rb @@ -302,23 +302,39 @@ def run_query(args) end context 'web_path' do - before do - subject + shared_examples 'return web_path correctly' do + it 'returns web_path correctly' do + expect(graphql_data_at(:package, :_links, :web_path)).to eq("/#{project.full_path}/-/packages/#{composer_package.id}") + end end - it 'returns web_path correctly' do - expect(graphql_data_at(:package, :_links, :web_path)).to eq("/#{project.full_path}/-/packages/#{composer_package.id}") - end + context 'with status default' do + before do + subject + end - context 'with terraform module' do - let_it_be(:terraform_package) { create(:terraform_module_package, project: project) } + it_behaves_like 'return web_path correctly' - let(:package_global_id) { global_id_of(terraform_package) } + context 'with terraform module' do + let_it_be(:terraform_package) { create(:terraform_module_package, project: project) } - it 'returns web_path correctly' do - expect(graphql_data_at(:package, :_links, :web_path)).to eq("/#{project.full_path}/-/terraform_module_registry/#{terraform_package.id}") + let(:package_global_id) { global_id_of(terraform_package) } + + it 'returns web_path correctly' do + expect(graphql_data_at(:package, :_links, :web_path)).to eq("/#{project.full_path}/-/terraform_module_registry/#{terraform_package.id}") + end end end + + context 'with status deprecated' do + before do + composer_package.deprecated! + + subject + end + + it_behaves_like 'return web_path correctly' + end end context 'public_package' do -- GitLab From 934f745f2f2d1546dd55ecf406df2a28828489f9 Mon Sep 17 00:00:00 2001 From: Dzmitry Meshcharakou <12459192-dmeshcharakou@users.noreply.gitlab.com> Date: Mon, 16 Dec 2024 10:33:48 +0100 Subject: [PATCH 3/7] Update documentation --- doc/user/packages/npm_registry/index.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/user/packages/npm_registry/index.md b/doc/user/packages/npm_registry/index.md index a9103fa12620c4..ec7ff7bf30c39a 100644 --- a/doc/user/packages/npm_registry/index.md +++ b/doc/user/packages/npm_registry/index.md @@ -455,6 +455,8 @@ npm deprecate @scope/package@1.0.1 "Only version 1.0.1 is deprecated" npm deprecate @scope/package@"< 1.0.5" "All package versions less than 1.0.5 are deprecated" ``` +When a package is deprecated, its status will be updated to `deprecated`. + ### Remove deprecation warning To remove a package's deprecation warning, specify `""` (an empty string) for the message. For example: @@ -463,6 +465,8 @@ To remove a package's deprecation warning, specify `""` (an empty string) for th npm deprecate @scope/package "" ``` +When a package's deprecation warning is removed, a package's status will be updated to `default`. + ## Helpful hints ### Install npm packages from other organizations -- GitLab From f6be212e8a82e30be4ddaa90c9c3089a1a12706a Mon Sep 17 00:00:00 2001 From: Dzmitry Meshcharakou <12459192-dmeshcharakou@users.noreply.gitlab.com> Date: Mon, 16 Dec 2024 12:50:10 +0100 Subject: [PATCH 4/7] Use pluck --- app/services/packages/npm/deprecate_package_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/packages/npm/deprecate_package_service.rb b/app/services/packages/npm/deprecate_package_service.rb index bd91385dfee9c2..ad1914087555e9 100644 --- a/app/services/packages/npm/deprecate_package_service.rb +++ b/app/services/packages/npm/deprecate_package_service.rb @@ -18,7 +18,7 @@ def execute attributes = relation.preload_npm_metadatum.filter_map { |package| metadatum_attributes(package) } next if attributes.empty? - package_ids = attributes.reduce([]) { |memo, attr| memo << attr[:package_id] } + package_ids = attributes.pluck(:package_id) # rubocop:disable CodeReuse/ActiveRecord, Database/AvoidUsingPluckWithoutLimit -- This is a hash, not an ActiveRecord relation. ApplicationRecord.transaction do ::Packages::Npm::Metadatum.upsert_all(attributes) -- GitLab From 009b7e43a569fc714eb4e9e1ccb1f94f5acf1664 Mon Sep 17 00:00:00 2001 From: Dzmitry Meshcharakou <12459192-dmeshcharakou@users.noreply.gitlab.com> Date: Tue, 17 Dec 2024 07:39:28 +0100 Subject: [PATCH 5/7] Update documentation --- doc/api/packages.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/packages.md b/doc/api/packages.md index 7bdfb31472e66c..1363259c5c94cd 100644 --- a/doc/api/packages.md +++ b/doc/api/packages.md @@ -20,7 +20,7 @@ The API documentation of [GitLab Packages](../administration/packages/index.md). Get a list of project packages. All package types are included in results. When accessed without authentication, only packages of public projects are returned. -By default, packages with `default`, `deprecated` and `error` status are returned. Use the `status` parameter to view other +By default, packages with `default`, `deprecated`, and `error` status are returned. Use the `status` parameter to view other packages. ```plaintext @@ -89,7 +89,7 @@ can result in malformed data or broken packages. Get a list of project packages at the group level. When accessed without authentication, only packages of public projects are returned. -By default, packages with `default`, `deprecated` and `error` status are returned. Use the `status` parameter to view other +By default, packages with `default`, `deprecated`, and `error` status are returned. Use the `status` parameter to view other packages. ```plaintext -- GitLab From 9f763e8c1785fd301581168a13ea9740e4785591 Mon Sep 17 00:00:00 2001 From: Dzmitry Meshcharakou <12459192-dmeshcharakou@users.noreply.gitlab.com> Date: Tue, 17 Dec 2024 11:33:06 +0100 Subject: [PATCH 6/7] Fix tests --- .../update_status_for_deprecated_npm_packages_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/lib/gitlab/background_migration/update_status_for_deprecated_npm_packages_spec.rb b/spec/lib/gitlab/background_migration/update_status_for_deprecated_npm_packages_spec.rb index f426087eadb2bd..f2d7946200b092 100644 --- a/spec/lib/gitlab/background_migration/update_status_for_deprecated_npm_packages_spec.rb +++ b/spec/lib/gitlab/background_migration/update_status_for_deprecated_npm_packages_spec.rb @@ -5,7 +5,9 @@ RSpec.describe Gitlab::BackgroundMigration::UpdateStatusForDeprecatedNpmPackages, feature_category: :package_registry do let!(:organization) { table(:organizations).create!(name: 'organization', path: 'organization') } - let!(:namespace) { table(:namespaces).create!(name: 'group', path: 'group', type: 'Group') } + let!(:namespace) do + table(:namespaces).create!(name: 'group', path: 'group', type: 'Group', organization_id: organization.id) + end let!(:project) do table(:projects).create!(name: 'project', path: 'project', project_namespace_id: namespace.id, -- GitLab From b80d9c435964ef1713dce157703805af06a46925 Mon Sep 17 00:00:00 2001 From: "Dzmitry (Dima) Meshcharakou" <12459192-dmeshcharakou@users.noreply.gitlab.com> Date: Tue, 17 Dec 2024 14:55:45 +0000 Subject: [PATCH 7/7] Update docs --- doc/user/packages/npm_registry/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/packages/npm_registry/index.md b/doc/user/packages/npm_registry/index.md index ec7ff7bf30c39a..57ad3856d3e06e 100644 --- a/doc/user/packages/npm_registry/index.md +++ b/doc/user/packages/npm_registry/index.md @@ -465,7 +465,7 @@ To remove a package's deprecation warning, specify `""` (an empty string) for th npm deprecate @scope/package "" ``` -When a package's deprecation warning is removed, a package's status will be updated to `default`. +When a package's deprecation warning is removed, its status will be updated to `default`. ## Helpful hints -- GitLab