diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-07 00:09:59 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-07 00:09:59 +0300 |
commit | a5f3c50a6b451c6b7709cd632e2917c71ba25391 (patch) | |
tree | b87884c44e1bf43728e1e08241fc61094fbeabaf | |
parent | 958f41148d08b03a9bbe37adecd6e3b0b10a7219 (diff) |
Add latest changes from gitlab-org/gitlab@master
8 files changed, 179 insertions, 0 deletions
diff --git a/app/assets/javascripts/pages/groups/settings/repository/create_deploy_token/index.js b/app/assets/javascripts/pages/groups/settings/repository/create_deploy_token/index.js index 6a7c6028c95..1943704ac3d 100644 --- a/app/assets/javascripts/pages/groups/settings/repository/create_deploy_token/index.js +++ b/app/assets/javascripts/pages/groups/settings/repository/create_deploy_token/index.js @@ -1 +1,6 @@ +// This "page" is only rendered as response to the create_deploy_token form. +// It shows the secret token to the user one time, but is otherwise identical +// with the Settings/Repository page. +// +// This is why we just import the other page's JavaScript here. import '../show/index'; diff --git a/app/graphql/mutations/ci/job_artifact/destroy.rb b/app/graphql/mutations/ci/job_artifact/destroy.rb new file mode 100644 index 00000000000..47b3535d631 --- /dev/null +++ b/app/graphql/mutations/ci/job_artifact/destroy.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Mutations + module Ci + module JobArtifact + class Destroy < BaseMutation + graphql_name 'ArtifactDestroy' + + authorize :destroy_artifacts + + ArtifactID = ::Types::GlobalIDType[::Ci::JobArtifact] + + argument :id, + ArtifactID, + required: true, + description: 'ID of the artifact to delete.' + + field :artifact, + Types::Ci::JobArtifactType, + null: true, + description: 'Deleted artifact.' + + def find_object(id: ) + GlobalID::Locator.locate(id) + end + + def resolve(id:) + artifact = authorized_find!(id: id) + + if artifact.destroy + { errors: [] } + else + { errors: artifact.errors.full_messages } + end + end + end + end + end +end diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index 3cd7d612bc0..ea833b35085 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -125,6 +125,7 @@ module Types mount_mutation Mutations::Ci::Job::Retry mount_mutation Mutations::Ci::Job::Cancel mount_mutation Mutations::Ci::Job::Unschedule + mount_mutation Mutations::Ci::JobArtifact::Destroy mount_mutation Mutations::Ci::JobTokenScope::AddProject mount_mutation Mutations::Ci::JobTokenScope::RemoveProject mount_mutation Mutations::Ci::Runner::Update diff --git a/app/policies/ci/job_artifact_policy.rb b/app/policies/ci/job_artifact_policy.rb new file mode 100644 index 00000000000..e25c7311565 --- /dev/null +++ b/app/policies/ci/job_artifact_policy.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module Ci + class JobArtifactPolicy < BasePolicy + delegate { @subject.job.project } + end +end diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 0b6591136ec..0e67d5d24ae 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -742,6 +742,25 @@ Input type: `ApiFuzzingCiConfigurationCreateInput` | <a id="mutationapifuzzingciconfigurationcreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | | <a id="mutationapifuzzingciconfigurationcreategitlabciyamleditpath"></a>`gitlabCiYamlEditPath` **{warning-solid}** | [`String`](#string) | **Deprecated:** The configuration snippet is now generated client-side. Deprecated in 14.6. | +### `Mutation.artifactDestroy` + +Input type: `ArtifactDestroyInput` + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mutationartifactdestroyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| <a id="mutationartifactdestroyid"></a>`id` | [`CiJobArtifactID!`](#cijobartifactid) | ID of the artifact to delete. | + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mutationartifactdestroyartifact"></a>`artifact` | [`CiJobArtifact`](#cijobartifact) | Deleted artifact. | +| <a id="mutationartifactdestroyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| <a id="mutationartifactdestroyerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | + ### `Mutation.auditEventsStreamingHeadersCreate` Input type: `AuditEventsStreamingHeadersCreateInput` diff --git a/lib/gitlab/database/partitioning_migration_helpers/index_helpers.rb b/lib/gitlab/database/partitioning_migration_helpers/index_helpers.rb index c9a3b5caf79..15b542cf089 100644 --- a/lib/gitlab/database/partitioning_migration_helpers/index_helpers.rb +++ b/lib/gitlab/database/partitioning_migration_helpers/index_helpers.rb @@ -77,8 +77,42 @@ module Gitlab end end + # Finds duplicate indexes for a given schema and table. This finds + # indexes where the index definition is identical but the names are + # different. Returns an array of arrays containing duplicate index name + # pairs. + # + # Example: + # + # find_duplicate_indexes('table_name_goes_here') + def find_duplicate_indexes(table_name, schema_name: connection.current_schema) + find_indexes(table_name, schema_name: schema_name) + .group_by { |r| r['index_id'] } + .select { |_, v| v.size > 1 } + .map { |_, indexes| indexes.map { |index| index['index_name'] } } + end + private + def find_indexes(table_name, schema_name: connection.current_schema) + indexes = connection.select_all(<<~SQL, 'SQL', [schema_name, table_name]) + SELECT n.nspname AS schema_name, + c.relname AS table_name, + i.relname AS index_name, + regexp_replace(pg_get_indexdef(i.oid), 'INDEX .*? USING', '_') AS index_id + FROM pg_index x + JOIN pg_class c ON c.oid = x.indrelid + JOIN pg_class i ON i.oid = x.indexrelid + LEFT JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE (c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"])) + AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"])) + AND n.nspname = $1 + AND c.relname = $2; + SQL + + indexes.to_a + end + def find_partitioned_table(table_name) partitioned_table = Gitlab::Database::PostgresPartitionedTable.find_by_name_in_current_schema(table_name) diff --git a/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb b/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb index edb8ae36c45..7465f69b87c 100644 --- a/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb +++ b/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb @@ -26,6 +26,7 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do CREATE TABLE #{table_name} ( id serial NOT NULL, created_at timestamptz NOT NULL, + updated_at timestamptz NOT NULL, PRIMARY KEY (id, created_at) ) PARTITION BY RANGE (created_at); @@ -204,4 +205,30 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do end end end + + describe '#find_duplicate_indexes' do + context 'when duplicate and non-duplicate indexes exist' do + let(:nonduplicate_column_name) { 'updated_at' } + let(:nonduplicate_index_name) { 'updated_at_idx' } + let(:duplicate_column_name) { 'created_at' } + let(:duplicate_index_name1) { 'created_at_idx' } + let(:duplicate_index_name2) { 'index_on_created_at' } + + before do + connection.execute(<<~SQL) + CREATE INDEX #{nonduplicate_index_name} ON #{table_name} (#{nonduplicate_column_name}); + CREATE INDEX #{duplicate_index_name1} ON #{table_name} (#{duplicate_column_name}); + CREATE INDEX #{duplicate_index_name2} ON #{table_name} (#{duplicate_column_name}); + SQL + end + + subject do + migration.find_duplicate_indexes(table_name) + end + + it 'finds the duplicate index' do + expect(subject).to match_array([match_array([duplicate_index_name1, duplicate_index_name2])]) + end + end + end end diff --git a/spec/requests/api/graphql/mutations/ci/job_artifact/destroy_spec.rb b/spec/requests/api/graphql/mutations/ci/job_artifact/destroy_spec.rb new file mode 100644 index 00000000000..a5ec9ea343d --- /dev/null +++ b/spec/requests/api/graphql/mutations/ci/job_artifact/destroy_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'ArtifactDestroy' do + include GraphqlHelpers + + let(:user) { create(:user) } + let(:artifact) { create(:ci_job_artifact) } + + let(:mutation) do + variables = { + id: artifact.to_global_id.to_s + } + graphql_mutation(:artifact_destroy, variables, 'errors') + end + + it 'returns an error if the user is not allowed to destroy the artifact' do + post_graphql_mutation(mutation, current_user: user) + + expect(graphql_errors).not_to be_empty + end + + context 'when the user is allowed to destroy the artifact' do + before do + artifact.job.project.add_maintainer(user) + end + + it 'destroys the artifact' do + post_graphql_mutation(mutation, current_user: user) + + expect(response).to have_gitlab_http_status(:success) + expect { artifact.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + + it 'returns error if destory fails' do + allow_next_found_instance_of(Ci::JobArtifact) do |instance| + allow(instance).to receive(:destroy).and_return(false) + allow(instance).to receive_message_chain(:errors, :full_messages).and_return(['cannot be removed']) + end + + post_graphql_mutation(mutation, current_user: user) + + expect(graphql_data_at(:artifact_destroy, :errors)).to contain_exactly('cannot be removed') + end + end +end |