e
- raise Gitlab::Graphql::Errors::ArgumentError, e.message
- end
-
- private
-
- def find_object(group_path)
- ::Group.find_by_full_path(group_path)
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/external_audit_event_destinations/destroy.rb b/ee/app/graphql/mutations/audit_events/external_audit_event_destinations/destroy.rb
deleted file mode 100644
index f01d401342bc202f34c6925e0507127f2ac9d139..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/external_audit_event_destinations/destroy.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module ExternalAuditEventDestinations
- class Destroy < Base
- graphql_name 'ExternalAuditEventDestinationDestroy'
-
- authorize :admin_external_audit_events
-
- argument :id, ::Types::GlobalIDType[::AuditEvents::ExternalAuditEventDestination],
- required: true,
- description: 'ID of external audit event destination to destroy.'
-
- def resolve(id:)
- destination = authorized_find!(id)
- paired_destination = destination.stream_destination
-
- if destination.destroy
- audit(destination, action: :destroy)
-
- paired_destination&.destroy
- end
-
- {
- external_audit_event_destination: nil,
- errors: []
- }
- end
-
- private
-
- def find_object(destination_gid)
- GitlabSchema.object_from_id(destination_gid, expected_type: ::AuditEvents::ExternalAuditEventDestination).sync
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/external_audit_event_destinations/update.rb b/ee/app/graphql/mutations/audit_events/external_audit_event_destinations/update.rb
deleted file mode 100644
index 83eed69dd25ebd5a585ca7f468f32e73e6cb1901..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/external_audit_event_destinations/update.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module ExternalAuditEventDestinations
- class Update < Base
- graphql_name 'ExternalAuditEventDestinationUpdate'
-
- include ::AuditEvents::Changes
- include ::AuditEvents::LegacyDestinationSyncHelper
-
- UPDATE_EVENT_NAME = 'update_event_streaming_destination'
- AUDIT_EVENT_COLUMNS = [:destination_url, :name, :active].freeze
-
- authorize :admin_external_audit_events
-
- argument :id, ::Types::GlobalIDType[::AuditEvents::ExternalAuditEventDestination],
- required: true,
- description: 'ID of external audit event destination to update.'
-
- argument :destination_url, GraphQL::Types::String,
- required: false,
- description: 'Destination URL to change.'
-
- argument :name, GraphQL::Types::String,
- required: false,
- description: 'Destination name.'
-
- argument :active, GraphQL::Types::Boolean,
- required: false,
- description: 'Active status of the destination.'
-
- field :external_audit_event_destination, ::Types::AuditEvents::ExternalAuditEventDestinationType,
- null: true,
- description: 'Updated destination.'
-
- def resolve(id:, destination_url: nil, name: nil, active: nil)
- destination = authorized_find!(id)
-
- destination_attributes = {
- destination_url: destination_url,
- name: name,
- active: active
- }.compact
-
- if destination.update(destination_attributes)
- audit_update(destination)
- update_stream_destination(legacy_destination_model: destination)
- end
-
- {
- external_audit_event_destination: (destination if destination.persisted?),
- errors: Array(destination.errors)
- }
- end
-
- private
-
- def audit_update(destination)
- AUDIT_EVENT_COLUMNS.each do |column|
- next unless destination.saved_change_to_attribute?(column)
-
- audit_changes(
- column,
- as: column.to_s,
- entity: destination.group,
- model: destination,
- event_type: UPDATE_EVENT_NAME
- )
- end
- end
-
- def find_object(destination_gid)
- GitlabSchema.object_from_id(destination_gid, expected_type: ::AuditEvents::ExternalAuditEventDestination).sync
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/google_cloud_logging_configurations/base.rb b/ee/app/graphql/mutations/audit_events/google_cloud_logging_configurations/base.rb
deleted file mode 100644
index 2e5f67cc4e1c24c350aea651b177a4eea7fcd6d4..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/google_cloud_logging_configurations/base.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module GoogleCloudLoggingConfigurations
- class Base < BaseMutation
- private
-
- def audit(config, action:)
- audit_context = {
- name: "google_cloud_logging_configuration_#{action}",
- author: current_user,
- scope: config.group,
- target: config.group,
- message: "#{action.capitalize} Google Cloud logging configuration with name: #{config.name} project id: " \
- "#{config.google_project_id_name} and log id: #{config.log_id_name}"
- }
-
- ::Gitlab::Audit::Auditor.audit(audit_context)
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/google_cloud_logging_configurations/common_update.rb b/ee/app/graphql/mutations/audit_events/google_cloud_logging_configurations/common_update.rb
deleted file mode 100644
index 02a34e6864331e38f1c7a30e0cd3ba34665817f4..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/google_cloud_logging_configurations/common_update.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module GoogleCloudLoggingConfigurations
- module CommonUpdate
- extend ActiveSupport::Concern
- include ::AuditEvents::LegacyDestinationSyncHelper
-
- AUDIT_EVENT_COLUMNS = [:google_project_id_name, :client_email, :private_key, :log_id_name, :name,
- :active].freeze
-
- included do
- include ::AuditEvents::Changes
-
- argument :name, GraphQL::Types::String,
- required: false,
- description: 'Destination name.'
-
- argument :google_project_id_name, GraphQL::Types::String,
- required: false,
- description: 'Unique identifier of the Google Cloud project ' \
- 'to which the logging configuration belongs.'
-
- argument :client_email, GraphQL::Types::String,
- required: false,
- description: 'Email address associated with the service account ' \
- 'that will be used to authenticate and interact with the ' \
- 'Google Cloud Logging service. This is part of the IAM credentials.'
-
- argument :log_id_name, GraphQL::Types::String,
- required: false,
- description: 'Unique identifier used to distinguish and manage ' \
- 'different logs within the same Google Cloud project.'
-
- argument :private_key, GraphQL::Types::String,
- required: false,
- description: 'Private Key associated with the service account. This key ' \
- 'is used to authenticate the service account and authorize it ' \
- 'to interact with the Google Cloud Logging service.'
-
- argument :active, GraphQL::Types::Boolean,
- required: false,
- description: 'Active status of the destination.'
- end
-
- def update_config(
- id:,
- google_project_id_name: nil, client_email: nil, private_key: nil, log_id_name: nil, name: nil, active: nil
- )
- config = authorized_find!(id)
-
- config_attributes = {
- google_project_id_name: google_project_id_name,
- client_email: client_email,
- private_key: private_key,
- log_id_name: log_id_name,
- name: name,
- active: active
- }.compact
-
- if config.update(config_attributes)
- audit_update(config)
- update_stream_destination(legacy_destination_model: config)
- [config, []]
- else
- [nil, Array(config.errors)]
- end
- end
-
- private
-
- def audit_update(destination)
- event_name = self.class::UPDATE_EVENT_NAME
- AUDIT_EVENT_COLUMNS.each do |column|
- next unless destination.saved_change_to_attribute?(column)
-
- audit_changes(
- column,
- as: column.to_s,
- entity: entity_for_model(destination),
- model: destination,
- event_type: event_name
- )
- end
- end
-
- def entity_for_model(config)
- if config.is_a?(::AuditEvents::Instance::GoogleCloudLoggingConfiguration)
- Gitlab::Audit::InstanceScope.new
- else
- config.group
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/google_cloud_logging_configurations/create.rb b/ee/app/graphql/mutations/audit_events/google_cloud_logging_configurations/create.rb
deleted file mode 100644
index 91782aeb50630753c22cd011c58e8dc2dbfa7dcf..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/google_cloud_logging_configurations/create.rb
+++ /dev/null
@@ -1,81 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module GoogleCloudLoggingConfigurations
- class Create < Base
- graphql_name 'GoogleCloudLoggingConfigurationCreate'
-
- include ::AuditEvents::LegacyDestinationSyncHelper
-
- authorize :admin_external_audit_events
-
- argument :name, GraphQL::Types::String,
- required: false,
- description: 'Destination name.'
-
- argument :group_path, GraphQL::Types::ID,
- required: true,
- description: 'Group path.'
-
- argument :google_project_id_name, GraphQL::Types::String,
- required: true,
- description: 'Unique identifier of the Google Cloud project ' \
- 'to which the logging configuration belongs.'
-
- argument :client_email, GraphQL::Types::String,
- required: true,
- description: 'Email address associated with the service account ' \
- 'that will be used to authenticate and interact with the ' \
- 'Google Cloud Logging service. This is part of the IAM credentials.'
-
- argument :log_id_name, GraphQL::Types::String,
- required: false,
- description: 'Unique identifier used to distinguish and manage ' \
- 'different logs within the same Google Cloud project.' \
- '(defaults to `audit_events`).',
- default_value: 'audit_events'
-
- argument :private_key, GraphQL::Types::String,
- required: true,
- description: 'Private Key associated with the service account. This key ' \
- 'is used to authenticate the service account and authorize it ' \
- 'to interact with the Google Cloud Logging service.'
- field :google_cloud_logging_configuration, ::Types::AuditEvents::GoogleCloudLoggingConfigurationType,
- null: true,
- description: 'configuration created.'
-
- def resolve(group_path:, google_project_id_name:, client_email:, private_key:, log_id_name: nil, name: nil)
- group = authorized_find!(group_path)
- config_attributes = {
- group: group,
- google_project_id_name: google_project_id_name,
- client_email: client_email,
- private_key: private_key,
- name: name
- }
-
- config_attributes[:log_id_name] = log_id_name if log_id_name.present?
-
- config = ::AuditEvents::GoogleCloudLoggingConfiguration.new(config_attributes)
-
- if config.save
- audit(config, action: :created)
-
- create_stream_destination(legacy_destination_model: config, category: :gcp, is_instance: false)
-
- { google_cloud_logging_configuration: config, errors: [] }
- else
- { google_cloud_logging_configuration: nil, errors: Array(config.errors) }
- end
- end
-
- private
-
- def find_object(group_path)
- ::Group.find_by_full_path(group_path)
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/google_cloud_logging_configurations/destroy.rb b/ee/app/graphql/mutations/audit_events/google_cloud_logging_configurations/destroy.rb
deleted file mode 100644
index 894ef9247df8ffc5f2de79d87ac8b457f6a068d0..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/google_cloud_logging_configurations/destroy.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module GoogleCloudLoggingConfigurations
- class Destroy < Base
- graphql_name 'GoogleCloudLoggingConfigurationDestroy'
-
- authorize :admin_external_audit_events
-
- argument :id, ::Types::GlobalIDType[::AuditEvents::GoogleCloudLoggingConfiguration],
- required: true,
- description: 'ID of the Google Cloud logging configuration to destroy.'
-
- def resolve(id:)
- config = authorized_find!(id)
- paired_destination = config.stream_destination
-
- if config.destroy
- audit(config, action: :deleted)
-
- paired_destination&.destroy
-
- { errors: [] }
- else
- { errors: Array(config.errors) }
- end
- end
-
- private
-
- def find_object(config_gid)
- GitlabSchema.object_from_id(
- config_gid,
- expected_type: ::AuditEvents::GoogleCloudLoggingConfiguration).sync
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/google_cloud_logging_configurations/update.rb b/ee/app/graphql/mutations/audit_events/google_cloud_logging_configurations/update.rb
deleted file mode 100644
index f6a6e7fba1a904ce2b55d6b51af3e2d5951f2f12..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/google_cloud_logging_configurations/update.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module GoogleCloudLoggingConfigurations
- class Update < Base
- graphql_name 'GoogleCloudLoggingConfigurationUpdate'
-
- include Mutations::AuditEvents::GoogleCloudLoggingConfigurations::CommonUpdate
-
- UPDATE_EVENT_NAME = 'google_cloud_logging_configuration_updated'
- authorize :admin_external_audit_events
-
- argument :id, ::Types::GlobalIDType[::AuditEvents::GoogleCloudLoggingConfiguration],
- required: true,
- description: 'ID of the google Cloud configuration to update.'
-
- field :google_cloud_logging_configuration, ::Types::AuditEvents::GoogleCloudLoggingConfigurationType,
- null: true,
- description: 'configuration updated.'
-
- def resolve(
- id:, google_project_id_name: nil, client_email: nil, private_key: nil, log_id_name: nil, name: nil,
- active: nil)
- config, errors = update_config(id: id, google_project_id_name: google_project_id_name,
- client_email: client_email, private_key: private_key,
- log_id_name: log_id_name, name: name, active: active)
-
- { google_cloud_logging_configuration: config, errors: errors }
- end
-
- private
-
- def find_object(config_gid)
- GitlabSchema.object_from_id(
- config_gid,
- expected_type: ::AuditEvents::GoogleCloudLoggingConfiguration).sync
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/instance/amazon_s3_configurations/base.rb b/ee/app/graphql/mutations/audit_events/instance/amazon_s3_configurations/base.rb
deleted file mode 100644
index 702aa485fd740b5fe133e6b800e34d2000433a2e..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/instance/amazon_s3_configurations/base.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Instance
- module AmazonS3Configurations
- class Base < BaseMutation
- authorize :admin_instance_external_audit_events
-
- def ready?(**args)
- raise_resource_not_available_error! unless current_user&.can?(:admin_instance_external_audit_events)
-
- super
- end
-
- private
-
- def audit(config, action:)
- audit_context = {
- name: "instance_amazon_s3_configuration_#{action}",
- author: current_user,
- scope: Gitlab::Audit::InstanceScope.new,
- target: config,
- message: "#{action.capitalize} Instance Amazon S3 configuration with name: #{config.name} " \
- "bucket: #{config.bucket_name} and AWS region: #{config.aws_region}"
- }
-
- ::Gitlab::Audit::Auditor.audit(audit_context)
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/instance/amazon_s3_configurations/create.rb b/ee/app/graphql/mutations/audit_events/instance/amazon_s3_configurations/create.rb
deleted file mode 100644
index 34b3ee8fbcc9239c8b3f00ae57d5c42128c97cdb..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/instance/amazon_s3_configurations/create.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Instance
- module AmazonS3Configurations
- class Create < Base
- graphql_name 'AuditEventsInstanceAmazonS3ConfigurationCreate'
-
- include ::AuditEvents::LegacyDestinationSyncHelper
-
- argument :name, GraphQL::Types::String,
- required: false,
- description: 'Destination name.'
-
- argument :access_key_xid, GraphQL::Types::String,
- required: true,
- description: 'Access key ID of the Amazon S3 account.'
-
- argument :secret_access_key, GraphQL::Types::String,
- required: true,
- description: 'Secret access key of the Amazon S3 account.'
-
- argument :bucket_name, GraphQL::Types::String,
- required: true,
- description: 'Name of the bucket where the audit events would be logged.'
-
- argument :aws_region, GraphQL::Types::String,
- required: true,
- description: 'AWS region where the bucket is created.'
-
- field :instance_amazon_s3_configuration, ::Types::AuditEvents::Instance::AmazonS3ConfigurationType,
- null: true,
- description: 'Created instance Amazon S3 configuration.'
-
- def resolve(access_key_xid:, secret_access_key:, bucket_name:, aws_region:, name: nil)
- config_attributes = {
- access_key_xid: access_key_xid,
- secret_access_key: secret_access_key,
- bucket_name: bucket_name,
- aws_region: aws_region,
- name: name
- }
-
- config = ::AuditEvents::Instance::AmazonS3Configuration.new(config_attributes)
-
- if config.save
- audit(config, action: :created)
-
- create_stream_destination(legacy_destination_model: config, category: :aws, is_instance: true)
-
- { instance_amazon_s3_configuration: config, errors: [] }
- else
- { instance_amazon_s3_configuration: nil, errors: Array(config.errors) }
- end
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/instance/amazon_s3_configurations/delete.rb b/ee/app/graphql/mutations/audit_events/instance/amazon_s3_configurations/delete.rb
deleted file mode 100644
index 620518c98f3c87efcaa38c2214bdac94db31b581..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/instance/amazon_s3_configurations/delete.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Instance
- module AmazonS3Configurations
- class Delete < Base
- graphql_name 'AuditEventsInstanceAmazonS3ConfigurationDelete'
-
- argument :id, ::Types::GlobalIDType[::AuditEvents::Instance::AmazonS3Configuration],
- required: true,
- description: 'ID of the instance-level Amazon S3 configuration to delete.'
-
- def resolve(id:)
- config = authorized_find!(id: id)
- paired_destination = config.stream_destination
-
- if config.destroy
- audit(config, action: :deleted)
-
- paired_destination&.destroy
- end
-
- { errors: Array(config.errors) }
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/instance/amazon_s3_configurations/update.rb b/ee/app/graphql/mutations/audit_events/instance/amazon_s3_configurations/update.rb
deleted file mode 100644
index a69e80caf6b011b5451cb8fbae3096340e83690c..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/instance/amazon_s3_configurations/update.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Instance
- module AmazonS3Configurations
- class Update < Base
- graphql_name 'AuditEventsInstanceAmazonS3ConfigurationUpdate'
-
- include ::AuditEvents::Changes
- include ::AuditEvents::LegacyDestinationSyncHelper
-
- UPDATE_EVENT_NAME = 'instance_amazon_s3_configuration_updated'
- AUDIT_EVENT_COLUMNS = [:access_key_xid, :secret_access_key, :bucket_name, :aws_region, :name, :active].freeze
-
- argument :id, ::Types::GlobalIDType[::AuditEvents::Instance::AmazonS3Configuration],
- required: true,
- description: 'ID of the instance-level Amazon S3 configuration to update.'
-
- argument :name, GraphQL::Types::String,
- required: false,
- description: 'Destination name.'
-
- argument :access_key_xid, GraphQL::Types::String,
- required: false,
- description: 'Access key ID of the Amazon S3 account.'
-
- argument :secret_access_key, GraphQL::Types::String,
- required: false,
- description: 'Secret access key of the Amazon S3 account.'
-
- argument :bucket_name, GraphQL::Types::String,
- required: false,
- description: 'Name of the bucket where the audit events would be logged.'
-
- argument :aws_region, GraphQL::Types::String,
- required: false,
- description: 'AWS region where the bucket is created.'
-
- argument :active, GraphQL::Types::Boolean,
- required: false,
- description: 'Active status of the destination.'
-
- field :instance_amazon_s3_configuration, ::Types::AuditEvents::Instance::AmazonS3ConfigurationType,
- null: true,
- description: 'Updated instance-level Amazon S3 configuration.'
-
- def resolve(
- id:, access_key_xid: nil, secret_access_key: nil, bucket_name: nil, aws_region: nil, name: nil, active: nil
- )
- config = authorized_find!(id: id)
-
- config_attributes = {
- access_key_xid: access_key_xid,
- secret_access_key: secret_access_key,
- bucket_name: bucket_name,
- aws_region: aws_region,
- name: name,
- active: active
- }.compact
-
- if config.update(config_attributes)
- audit_update(config)
- update_stream_destination(legacy_destination_model: config)
- { instance_amazon_s3_configuration: config, errors: [] }
- else
- { instance_amazon_s3_configuration: nil, errors: Array(config.errors) }
- end
- end
-
- private
-
- def audit_update(config)
- AUDIT_EVENT_COLUMNS.each do |column|
- audit_changes(
- column,
- as: column.to_s,
- entity: Gitlab::Audit::InstanceScope.new,
- model: config,
- event_type: UPDATE_EVENT_NAME
- )
- end
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/base.rb b/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/base.rb
deleted file mode 100644
index 1b10b196f5202d355c0d225de172e40730ec7ab7..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/base.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Instance
- module GoogleCloudLoggingConfigurations
- class Base < BaseMutation
- authorize :admin_instance_external_audit_events
-
- def ready?(**args)
- raise_resource_not_available_error! unless current_user&.can?(:admin_instance_external_audit_events)
-
- super
- end
-
- private
-
- def find_object(config_gid)
- destination = GitlabSchema.object_from_id(
- config_gid,
- expected_type: ::AuditEvents::Instance::GoogleCloudLoggingConfiguration).sync
-
- raise_resource_not_available_error! if destination.blank?
-
- destination
- end
-
- def audit(config, action:)
- audit_context = {
- name: "instance_google_cloud_logging_configuration_#{action}",
- author: current_user,
- scope: Gitlab::Audit::InstanceScope.new,
- target: config,
- message: "#{action.capitalize} Instance Google Cloud logging configuration with name: #{config.name} " \
- "project id: #{config.google_project_id_name} and log id: #{config.log_id_name}"
- }
-
- ::Gitlab::Audit::Auditor.audit(audit_context)
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/create.rb b/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/create.rb
deleted file mode 100644
index ca0600d0da3d78ed98aa2c7d174fca1016b922d8..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/create.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Instance
- module GoogleCloudLoggingConfigurations
- class Create < Base
- graphql_name 'InstanceGoogleCloudLoggingConfigurationCreate'
-
- include ::AuditEvents::LegacyDestinationSyncHelper
-
- argument :name, GraphQL::Types::String,
- required: false,
- description: 'Destination name.'
-
- argument :google_project_id_name, GraphQL::Types::String,
- required: true,
- description: 'Unique identifier of the Google Cloud project ' \
- 'to which the logging configuration belongs.'
-
- argument :client_email, GraphQL::Types::String,
- required: true,
- description: 'Email address associated with the service account ' \
- 'that will be used to authenticate and interact with the ' \
- 'Google Cloud Logging service. This is part of the IAM credentials.'
-
- argument :log_id_name, GraphQL::Types::String,
- required: false,
- description: 'Unique identifier used to distinguish and manage ' \
- 'different logs within the same Google Cloud project.' \
- '(defaults to `audit_events`).',
- default_value: 'audit_events'
-
- argument :private_key, GraphQL::Types::String,
- required: true,
- description: 'Private Key associated with the service account. This key ' \
- 'is used to authenticate the service account and authorize it ' \
- 'to interact with the Google Cloud Logging service.'
-
- field :instance_google_cloud_logging_configuration,
- ::Types::AuditEvents::Instance::GoogleCloudLoggingConfigurationType,
- null: true,
- description: 'configuration created.'
-
- def resolve(google_project_id_name:, client_email:, private_key:, log_id_name: nil, name: nil)
- config_attributes = {
- google_project_id_name: google_project_id_name,
- client_email: client_email,
- private_key: private_key,
- name: name
- }
-
- config_attributes[:log_id_name] = log_id_name if log_id_name.present?
-
- config = ::AuditEvents::Instance::GoogleCloudLoggingConfiguration.new(config_attributes)
-
- if config.save
- audit(config, action: :created)
-
- create_stream_destination(legacy_destination_model: config, category: :gcp, is_instance: true)
-
- { instance_google_cloud_logging_configuration: config, errors: [] }
- else
- { instance_google_cloud_logging_configuration: nil, errors: Array(config.errors) }
- end
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy.rb b/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy.rb
deleted file mode 100644
index f05ca622f0872fa5d14a640418b8002b1ee43cd7..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Instance
- module GoogleCloudLoggingConfigurations
- class Destroy < Base
- graphql_name 'InstanceGoogleCloudLoggingConfigurationDestroy'
-
- argument :id, ::Types::GlobalIDType[::AuditEvents::Instance::GoogleCloudLoggingConfiguration],
- required: true,
- description: 'ID of the Google Cloud logging configuration to destroy.'
-
- def resolve(id:)
- config = authorized_find!(id)
- paired_destination = config.stream_destination
-
- if config.destroy
- audit(config, action: :deleted)
-
- paired_destination&.destroy
- end
-
- { errors: Array(config.errors) }
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/update.rb b/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/update.rb
deleted file mode 100644
index 1c17edd8050f5b029bebf05e096dc8b1ba1bacf9..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/update.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Instance
- module GoogleCloudLoggingConfigurations
- class Update < Base
- graphql_name 'InstanceGoogleCloudLoggingConfigurationUpdate'
-
- include Mutations::AuditEvents::GoogleCloudLoggingConfigurations::CommonUpdate
-
- UPDATE_EVENT_NAME = 'instance_google_cloud_logging_configuration_updated'
-
- argument :id, ::Types::GlobalIDType[::AuditEvents::Instance::GoogleCloudLoggingConfiguration],
- required: true,
- description: 'ID of the instance google Cloud configuration to update.'
-
- field :instance_google_cloud_logging_configuration,
- ::Types::AuditEvents::Instance::GoogleCloudLoggingConfigurationType,
- null: true,
- description: 'configuration updated.'
-
- def resolve(
- id:, google_project_id_name: nil, client_email: nil, private_key: nil, log_id_name: nil, name: nil,
- active: nil)
- config, errors = update_config(id: id, google_project_id_name: google_project_id_name,
- client_email: client_email, private_key: private_key,
- log_id_name: log_id_name, name: name, active: active)
-
- { instance_google_cloud_logging_configuration: config, errors: errors }
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/instance_external_audit_event_destinations/base.rb b/ee/app/graphql/mutations/audit_events/instance_external_audit_event_destinations/base.rb
deleted file mode 100644
index 8768b78ac48eff2e9316c86e9640789b6233d38d..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/instance_external_audit_event_destinations/base.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module InstanceExternalAuditEventDestinations
- class Base < BaseMutation
- ERROR_MESSAGE = 'You do not have access to this mutation.'
-
- def ready?(**args)
- unless current_user&.can?(:admin_instance_external_audit_events)
- raise_resource_not_available_error! ERROR_MESSAGE
- end
-
- super
- end
-
- private
-
- def find_object(destination_gid)
- destination = GitlabSchema.object_from_id(
- destination_gid,
- expected_type: ::AuditEvents::InstanceExternalAuditEventDestination).sync
-
- raise_resource_not_available_error! if destination.blank?
-
- destination
- end
-
- def audit(destination, action:, extra_context: {})
- audit_context = {
- name: "#{action}_instance_event_streaming_destination",
- author: current_user,
- scope: Gitlab::Audit::InstanceScope.new,
- target: destination,
- message: "#{action.capitalize} instance event streaming destination #{destination.destination_url}"
- }
-
- ::Gitlab::Audit::Auditor.audit(audit_context.merge(extra_context))
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/instance_external_audit_event_destinations/create.rb b/ee/app/graphql/mutations/audit_events/instance_external_audit_event_destinations/create.rb
deleted file mode 100644
index 703c643f3fe7acb4dd1deb4187f4fe68944371b7..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/instance_external_audit_event_destinations/create.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module InstanceExternalAuditEventDestinations
- class Create < Base
- graphql_name 'InstanceExternalAuditEventDestinationCreate'
-
- include ::AuditEvents::LegacyDestinationSyncHelper
-
- authorize :admin_instance_external_audit_events
-
- argument :destination_url, GraphQL::Types::String,
- required: true,
- description: 'Destination URL.'
-
- argument :name, GraphQL::Types::String,
- required: false,
- description: 'Destination name.'
-
- field :instance_external_audit_event_destination,
- ::Types::AuditEvents::InstanceExternalAuditEventDestinationType,
- null: true,
- description: 'Destination created.'
-
- def resolve(destination_url:, name: nil)
- destination = ::AuditEvents::InstanceExternalAuditEventDestination.new(destination_url: destination_url,
- name: name)
-
- destination.save!
- audit(destination, action: :create)
- create_stream_destination(legacy_destination_model: destination, category: :http, is_instance: true)
-
- {
- instance_external_audit_event_destination: destination,
- errors: []
- }
-
- rescue ActiveRecord::RecordInvalid => e
- raise Gitlab::Graphql::Errors::ArgumentError, e.message
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/instance_external_audit_event_destinations/destroy.rb b/ee/app/graphql/mutations/audit_events/instance_external_audit_event_destinations/destroy.rb
deleted file mode 100644
index f3356c8cdedbee62ff3169235f8da874d7907c1b..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/instance_external_audit_event_destinations/destroy.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module InstanceExternalAuditEventDestinations
- class Destroy < Base
- graphql_name 'InstanceExternalAuditEventDestinationDestroy'
-
- authorize :admin_instance_external_audit_events
-
- argument :id, ::Types::GlobalIDType[::AuditEvents::InstanceExternalAuditEventDestination],
- required: true,
- description: 'ID of the external instance audit event destination to destroy.'
-
- def resolve(id:)
- destination = find_object(id)
- paired_destination = destination.stream_destination
-
- if destination.destroy
- audit(destination, action: :destroy)
-
- paired_destination&.destroy
- end
-
- {
- instance_external_audit_event_destination: nil,
- errors: []
- }
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/instance_external_audit_event_destinations/update.rb b/ee/app/graphql/mutations/audit_events/instance_external_audit_event_destinations/update.rb
deleted file mode 100644
index 487ca71dae4347f22a004d92440e086cf410bb35..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/instance_external_audit_event_destinations/update.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module InstanceExternalAuditEventDestinations
- class Update < Base
- graphql_name 'InstanceExternalAuditEventDestinationUpdate'
-
- include ::AuditEvents::Changes
- include ::AuditEvents::LegacyDestinationSyncHelper
-
- authorize :admin_instance_external_audit_events
-
- UPDATE_EVENT_NAME = 'update_instance_event_streaming_destination'
- AUDIT_EVENT_COLUMNS = [:destination_url, :name, :active].freeze
-
- argument :id, ::Types::GlobalIDType[::AuditEvents::InstanceExternalAuditEventDestination],
- required: true,
- description: 'ID of the external instance audit event destination to update.'
-
- argument :destination_url, GraphQL::Types::String,
- required: false,
- description: 'Destination URL to change.'
-
- argument :name, GraphQL::Types::String,
- required: false,
- description: 'Destination name.'
-
- argument :active, GraphQL::Types::Boolean,
- required: false,
- description: 'Active status of the destination.'
-
- field :instance_external_audit_event_destination,
- ::Types::AuditEvents::InstanceExternalAuditEventDestinationType,
- null: true,
- description: 'Updated destination.'
-
- def resolve(id:, destination_url: nil, name: nil, active: nil)
- destination = find_object(id)
-
- destination_attributes = { destination_url: destination_url, name: name, active: active }.compact
-
- if destination.update(destination_attributes)
- audit_update(destination)
- update_stream_destination(legacy_destination_model: destination)
- end
-
- {
- instance_external_audit_event_destination: (destination if destination.persisted?),
- errors: Array(destination.errors)
- }
- end
-
- private
-
- def audit_update(destination)
- AUDIT_EVENT_COLUMNS.each do |column|
- next unless destination.saved_change_to_attribute?(column)
-
- audit_changes(
- column,
- as: column.to_s,
- entity: Gitlab::Audit::InstanceScope.new,
- model: destination,
- event_type: UPDATE_EVENT_NAME
- )
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/streaming/event_type_filters/create.rb b/ee/app/graphql/mutations/audit_events/streaming/event_type_filters/create.rb
deleted file mode 100644
index 1458c5469602fff1a32a8ab58dcfc45ab2866149..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/streaming/event_type_filters/create.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Streaming
- module EventTypeFilters
- class Create < BaseEventTypeFilters::BaseCreate
- graphql_name 'AuditEventsStreamingDestinationEventsAdd'
- authorize :admin_external_audit_events
-
- argument :destination_id, ::Types::GlobalIDType[::AuditEvents::ExternalAuditEventDestination],
- required: true,
- description: 'Destination id.'
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/streaming/event_type_filters/destroy.rb b/ee/app/graphql/mutations/audit_events/streaming/event_type_filters/destroy.rb
deleted file mode 100644
index e7e4a282ed887855bed56b98f3a0356cca46b83b..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/streaming/event_type_filters/destroy.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Streaming
- module EventTypeFilters
- class Destroy < BaseEventTypeFilters::BaseDestroy
- graphql_name 'AuditEventsStreamingDestinationEventsRemove'
-
- authorize :admin_external_audit_events
-
- argument :destination_id, ::Types::GlobalIDType[::AuditEvents::ExternalAuditEventDestination],
- required: true,
- description: 'Destination id.'
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/streaming/headers/create.rb b/ee/app/graphql/mutations/audit_events/streaming/headers/create.rb
deleted file mode 100644
index df25d50159bcf4ffe7634461aa13e818c9e95a11..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/streaming/headers/create.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Streaming
- module Headers
- class Create < BaseMutation
- graphql_name 'AuditEventsStreamingHeadersCreate'
- authorize :admin_external_audit_events
-
- argument :key, GraphQL::Types::String,
- required: true,
- description: 'Header key.'
-
- argument :value, GraphQL::Types::String,
- required: true,
- description: 'Header value.'
-
- argument :destination_id, ::Types::GlobalIDType[::AuditEvents::ExternalAuditEventDestination],
- required: true,
- description: 'Destination to associate header with.'
-
- argument :active, GraphQL::Types::Boolean,
- required: false,
- default_value: true,
- description: 'Boolean option determining whether header is active or not.'
-
- field :header, ::Types::AuditEvents::Streaming::HeaderType,
- null: true,
- description: 'Created header.'
-
- def resolve(destination_id:, key:, value:, active:)
- response = ::AuditEvents::Streaming::Headers::CreateService.new(
- destination: authorized_find!(id: destination_id),
- params: { key: key, value: value, active: active },
- current_user: current_user
- ).execute
-
- if response.success?
- { header: response.payload[:header], errors: [] }
- else
- { header: nil, errors: response.errors }
- end
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/streaming/headers/destroy.rb b/ee/app/graphql/mutations/audit_events/streaming/headers/destroy.rb
deleted file mode 100644
index 3e89c0da3cec5ad92243a9be8a9624b85ca6ab5a..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/streaming/headers/destroy.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Streaming
- module Headers
- class Destroy < BaseMutation
- graphql_name 'AuditEventsStreamingHeadersDestroy'
- authorize :admin_external_audit_events
-
- argument :header_id, ::Types::GlobalIDType[::AuditEvents::Streaming::Header],
- required: true,
- description: 'Header to delete.'
-
- def resolve(header_id:)
- header = authorized_find!(id: header_id)
-
- response = ::AuditEvents::Streaming::Headers::DestroyService.new(
- destination: header.external_audit_event_destination,
- params: { header: header },
- current_user: current_user
- ).execute
-
- if response.success?
- { header: nil, errors: [] }
- else
- { header: header, errors: response.errors }
- end
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/streaming/headers/update.rb b/ee/app/graphql/mutations/audit_events/streaming/headers/update.rb
deleted file mode 100644
index 49c91798e42b4ecfc4a95bf41eec559916332e71..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/streaming/headers/update.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Streaming
- module Headers
- class Update < BaseMutation
- graphql_name 'AuditEventsStreamingHeadersUpdate'
- authorize :admin_external_audit_events
-
- argument :header_id, ::Types::GlobalIDType[::AuditEvents::Streaming::Header],
- required: true,
- description: 'Header to update.'
-
- argument :key, GraphQL::Types::String,
- required: false,
- default_value: nil,
- description: 'Header key.'
-
- argument :value, GraphQL::Types::String,
- required: false,
- default_value: nil,
- description: 'Header value.'
-
- argument :active, GraphQL::Types::Boolean,
- required: false,
- default_value: nil,
- description: 'Boolean option determining whether header is active or not.'
-
- field :header, ::Types::AuditEvents::Streaming::HeaderType,
- null: true,
- description: 'Updates header.'
-
- def resolve(header_id:, key:, value:, active:)
- header = authorized_find!(id: header_id)
-
- response = ::AuditEvents::Streaming::Headers::UpdateService.new(
- destination: header.external_audit_event_destination,
- params: { header: header, key: key, value: value, active: active },
- current_user: current_user
- ).execute
-
- if response.success?
- { header: response.payload[:header], errors: [] }
- else
- { header: header.reset, errors: response.errors }
- end
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/streaming/http/namespace_filters/base.rb b/ee/app/graphql/mutations/audit_events/streaming/http/namespace_filters/base.rb
deleted file mode 100644
index c37f376c3c113245774ab32f0666616552ad81b0..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/streaming/http/namespace_filters/base.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Streaming
- module HTTP
- module NamespaceFilters
- class Base < BaseMutation
- authorize :admin_external_audit_events
- include ::AuditEvents::NamespaceFilterSyncHelper
-
- private
-
- def audit(filter, action:)
- audit_context = {
- name: "#{action}_http_namespace_filter",
- author: current_user,
- scope: filter.external_audit_event_destination.group,
- target: filter.external_audit_event_destination,
- message: "#{action.capitalize} namespace filter for http audit event streaming destination " \
- "#{filter.external_audit_event_destination.name} and namespace #{filter.namespace.full_path}"
- }
-
- ::Gitlab::Audit::Auditor.audit(audit_context)
- end
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/streaming/http/namespace_filters/create.rb b/ee/app/graphql/mutations/audit_events/streaming/http/namespace_filters/create.rb
deleted file mode 100644
index 2ce511adeaddc87e65e2646cf3fe9483a7555c9c..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/streaming/http/namespace_filters/create.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Streaming
- module HTTP
- module NamespaceFilters
- class Create < Base
- graphql_name 'AuditEventsStreamingHTTPNamespaceFiltersAdd'
-
- argument :destination_id, ::Types::GlobalIDType[::AuditEvents::ExternalAuditEventDestination],
- required: true,
- description: 'Destination ID.'
-
- argument :group_path, GraphQL::Types::ID,
- required: false,
- description: 'Full path of the group.'
-
- argument :project_path, GraphQL::Types::ID,
- required: false,
- description: 'Full path of the project.'
-
- field :namespace_filter, ::Types::AuditEvents::Streaming::HTTP::NamespaceFilterType,
- null: true,
- description: 'Namespace filter created.'
-
- validates exactly_one_of: [:group_path, :project_path]
-
- def resolve(args)
- destination = authorized_find!(args[:destination_id])
-
- namespace = namespace(args[:group_path], args[:project_path])
-
- filter = ::AuditEvents::Streaming::HTTP::NamespaceFilter
- .new(external_audit_event_destination: destination,
- namespace: namespace)
-
- if filter.save
- sync_stream_namespace_filter(destination, namespace)
-
- audit(filter, action: :create)
- end
-
- { namespace_filter: (filter if filter.persisted?), errors: Array(filter.errors) }
- end
-
- private
-
- def find_object(destination_id)
- ::GitlabSchema.object_from_id(destination_id, expected_type: ::AuditEvents::ExternalAuditEventDestination)
- end
-
- def namespace(group_path, project_path)
- if group_path.present?
- namespace = ::Group.find_by_full_path(group_path)
- raise_resource_not_available_error! 'group_path is invalid' if namespace.nil?
- return namespace
- end
-
- namespace = ::Project.find_by_full_path(project_path)
- raise_resource_not_available_error! 'project_path is invalid' if namespace.nil?
- namespace.project_namespace
- end
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/streaming/http/namespace_filters/delete.rb b/ee/app/graphql/mutations/audit_events/streaming/http/namespace_filters/delete.rb
deleted file mode 100644
index a7ab713c23f6d80f7857a91a824df4889756dd5d..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/streaming/http/namespace_filters/delete.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Streaming
- module HTTP
- module NamespaceFilters
- class Delete < Base
- graphql_name 'AuditEventsStreamingHTTPNamespaceFiltersDelete'
-
- argument :namespace_filter_id, ::Types::GlobalIDType[::AuditEvents::Streaming::HTTP::NamespaceFilter],
- required: true,
- description: 'Namespace filter ID.'
-
- def resolve(namespace_filter_id:)
- filter = authorized_find!(id: namespace_filter_id)
-
- destination = filter.external_audit_event_destination
- should_sync = destination.stream_destination_id.present?
-
- if filter.destroy
- sync_delete_stream_namespace_filter(destination) if should_sync
- audit(filter, action: :delete)
- end
-
- { namespace_filter: nil, errors: [] }
- end
-
- private
-
- def find_object(id:)
- ::GitlabSchema.object_from_id(id, expected_type: ::AuditEvents::Streaming::HTTP::NamespaceFilter)
- end
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/streaming/instance_event_type_filters/create.rb b/ee/app/graphql/mutations/audit_events/streaming/instance_event_type_filters/create.rb
deleted file mode 100644
index d3623005b2f7bfb28dc8f2bbe97a659f5f2e0e55..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/streaming/instance_event_type_filters/create.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Streaming
- module InstanceEventTypeFilters
- class Create < BaseEventTypeFilters::BaseCreate
- graphql_name 'AuditEventsStreamingDestinationInstanceEventsAdd'
- authorize :admin_instance_external_audit_events
-
- argument :destination_id, ::Types::GlobalIDType[::AuditEvents::InstanceExternalAuditEventDestination],
- required: true,
- description: 'Destination id.'
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/streaming/instance_event_type_filters/destroy.rb b/ee/app/graphql/mutations/audit_events/streaming/instance_event_type_filters/destroy.rb
deleted file mode 100644
index 295731708f1d29ee850a3fa67409d6d570135897..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/streaming/instance_event_type_filters/destroy.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Streaming
- module InstanceEventTypeFilters
- class Destroy < BaseEventTypeFilters::BaseDestroy
- graphql_name 'AuditEventsStreamingDestinationInstanceEventsRemove'
-
- authorize :admin_instance_external_audit_events
-
- argument :destination_id, ::Types::GlobalIDType[::AuditEvents::InstanceExternalAuditEventDestination],
- required: true,
- description: 'Destination id.'
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/streaming/instance_headers/base.rb b/ee/app/graphql/mutations/audit_events/streaming/instance_headers/base.rb
deleted file mode 100644
index b2d0fbd78797a40758f1ab8b647c1a324f8d07ba..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/streaming/instance_headers/base.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Streaming
- module InstanceHeaders
- class Base < BaseMutation
- ERROR_MESSAGE = 'You do not have access to this mutation.'
- DESTINATION_ERROR_MESSAGE = 'Please provide valid destination id.'
-
- authorize :admin_instance_external_audit_events
-
- def ready?(**args)
- unless current_user&.can?(:admin_instance_external_audit_events)
- raise_resource_not_available_error! ERROR_MESSAGE
- end
-
- super
- end
-
- private
-
- def find_destination(destination_id)
- destination = GitlabSchema.object_from_id(
- destination_id, expected_type: ::AuditEvents::InstanceExternalAuditEventDestination
- ).sync
-
- raise_resource_not_available_error! DESTINATION_ERROR_MESSAGE if destination.blank?
-
- destination
- end
-
- def find_header(header_id)
- header = GitlabSchema.object_from_id(
- header_id, expected_type: ::AuditEvents::Streaming::InstanceHeader
- ).sync
-
- raise_resource_not_available_error! if header.blank?
-
- header
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/streaming/instance_headers/create.rb b/ee/app/graphql/mutations/audit_events/streaming/instance_headers/create.rb
deleted file mode 100644
index b718d05ecbb3cc3be2884a6cc66ba88994a0f6df..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/streaming/instance_headers/create.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Streaming
- module InstanceHeaders
- class Create < Base
- graphql_name 'AuditEventsStreamingInstanceHeadersCreate'
-
- argument :key, GraphQL::Types::String,
- required: true,
- description: 'Header key.'
-
- argument :value, GraphQL::Types::String,
- required: true,
- description: 'Header value.'
-
- argument :destination_id, ::Types::GlobalIDType[::AuditEvents::InstanceExternalAuditEventDestination],
- required: true,
- description: 'Instance level external destination to associate header with.'
-
- argument :active, GraphQL::Types::Boolean,
- required: false,
- default_value: true,
- description: 'Boolean option determining whether header is active or not.'
-
- field :header, ::Types::AuditEvents::Streaming::InstanceHeaderType,
- null: true,
- description: 'Created header.'
-
- def resolve(destination_id:, key:, value:, active:)
- response = ::AuditEvents::Streaming::InstanceHeaders::CreateService.new(
- params: { key: key, value: value, destination: find_destination(destination_id), active: active },
- current_user: current_user
- ).execute
-
- if response.success?
- { header: response.payload[:header], errors: [] }
- else
- { header: nil, errors: response.errors }
- end
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/streaming/instance_headers/destroy.rb b/ee/app/graphql/mutations/audit_events/streaming/instance_headers/destroy.rb
deleted file mode 100644
index 408fb473b23a8031d429003dd1b898d7769fa52c..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/streaming/instance_headers/destroy.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Streaming
- module InstanceHeaders
- class Destroy < Base
- graphql_name 'AuditEventsStreamingInstanceHeadersDestroy'
-
- argument :header_id, ::Types::GlobalIDType[::AuditEvents::Streaming::InstanceHeader],
- required: true,
- description: 'Header to delete.'
-
- def resolve(header_id:)
- header = authorized_find!(id: header_id)
-
- response = ::AuditEvents::Streaming::InstanceHeaders::DestroyService.new(
- params: { header: header },
- current_user: current_user
- ).execute
-
- if response.success?
- { header: nil, errors: [] }
- else
- { header: header, errors: response.errors }
- end
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/mutations/audit_events/streaming/instance_headers/update.rb b/ee/app/graphql/mutations/audit_events/streaming/instance_headers/update.rb
deleted file mode 100644
index f9dfa188e03525fd92e6e20c30b1f469722d9758..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/audit_events/streaming/instance_headers/update.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module AuditEvents
- module Streaming
- module InstanceHeaders
- class Update < Base
- graphql_name 'AuditEventsStreamingInstanceHeadersUpdate'
-
- argument :header_id, ::Types::GlobalIDType[::AuditEvents::Streaming::InstanceHeader],
- required: true,
- description: 'Header to update.'
-
- argument :key, GraphQL::Types::String,
- required: false,
- default_value: nil,
- description: 'Header key.'
-
- argument :value, GraphQL::Types::String,
- required: false,
- default_value: nil,
- description: 'Header value.'
-
- argument :active, GraphQL::Types::Boolean,
- required: false,
- default_value: nil,
- description: 'Boolean option determining whether header is active or not.'
-
- field :header, ::Types::AuditEvents::Streaming::InstanceHeaderType,
- null: true,
- description: 'Updates header.'
-
- def resolve(header_id:, key:, value:, active:)
- header = find_header(header_id)
-
- response = ::AuditEvents::Streaming::InstanceHeaders::UpdateService.new(
- params: { header: header, key: key, value: value, active: active },
- current_user: current_user
- ).execute
-
- if response.success?
- { header: response.payload[:header], errors: [] }
- elsif header.present?
- { header: header.reset, errors: response.errors }
- end
- end
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/resolvers/audit_events/instance/amazon_s3_configurations_resolver.rb b/ee/app/graphql/resolvers/audit_events/instance/amazon_s3_configurations_resolver.rb
deleted file mode 100644
index d5adfe10f1093279dee1c175295d2bce1b44a8c5..0000000000000000000000000000000000000000
--- a/ee/app/graphql/resolvers/audit_events/instance/amazon_s3_configurations_resolver.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-module Resolvers
- module AuditEvents
- module Instance
- class AmazonS3ConfigurationsResolver < BaseResolver
- type [::Types::AuditEvents::Instance::AmazonS3ConfigurationType], null: true
-
- def resolve
- ::AuditEvents::Instance::AmazonS3Configuration.all
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/resolvers/audit_events/instance/google_cloud_logging_configurations_resolver.rb b/ee/app/graphql/resolvers/audit_events/instance/google_cloud_logging_configurations_resolver.rb
deleted file mode 100644
index 02058d873fa011e4c2aa3a7d84c805e0c08a7719..0000000000000000000000000000000000000000
--- a/ee/app/graphql/resolvers/audit_events/instance/google_cloud_logging_configurations_resolver.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-module Resolvers
- module AuditEvents
- module Instance
- class GoogleCloudLoggingConfigurationsResolver < BaseResolver
- type [::Types::AuditEvents::Instance::GoogleCloudLoggingConfigurationType], null: true
-
- def resolve
- # There is a limit of maximum 5 GCL configs per instance and also for graphql queries there is a limit of 100
- # records per page so using `all` is ok here.
- ::AuditEvents::Instance::GoogleCloudLoggingConfiguration.all
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/resolvers/audit_events/instance_external_audit_event_destinations_resolver.rb b/ee/app/graphql/resolvers/audit_events/instance_external_audit_event_destinations_resolver.rb
deleted file mode 100644
index 6cbfc124756c8fe607648c027ca27710d75a6585..0000000000000000000000000000000000000000
--- a/ee/app/graphql/resolvers/audit_events/instance_external_audit_event_destinations_resolver.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-module Resolvers
- module AuditEvents
- class InstanceExternalAuditEventDestinationsResolver < BaseResolver
- include LooksAhead
-
- type [::Types::AuditEvents::InstanceExternalAuditEventDestinationType], null: true
-
- def resolve_with_lookahead
- apply_lookahead(::AuditEvents::InstanceExternalAuditEventDestination.all)
- end
-
- def preloads
- {
- headers: [:headers],
- event_type_filters: [:event_type_filters]
- }
- end
- end
- end
-end
diff --git a/ee/app/graphql/types/audit_events/amazon_s3_configuration_interface.rb b/ee/app/graphql/types/audit_events/amazon_s3_configuration_interface.rb
deleted file mode 100644
index 9b197c417196ef3eb32ed7a6448623fc4bd429b1..0000000000000000000000000000000000000000
--- a/ee/app/graphql/types/audit_events/amazon_s3_configuration_interface.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- module AuditEvents
- module AmazonS3ConfigurationInterface
- include Types::BaseInterface
-
- field :id, GraphQL::Types::ID,
- null: false,
- description: 'ID of the configuration.'
-
- field :name, GraphQL::Types::String,
- null: false,
- description: 'Name of the external destination to send audit events to.'
-
- field :access_key_xid, GraphQL::Types::String,
- null: false,
- description: 'Access key ID of the Amazon S3 account.'
-
- field :bucket_name, GraphQL::Types::String,
- null: false,
- description: 'Name of the bucket where the audit events would be logged.'
-
- field :aws_region, GraphQL::Types::String,
- null: false,
- description: 'AWS region where the bucket is created.'
-
- field :active, GraphQL::Types::Boolean,
- null: false,
- description: 'Active status of the destination.'
- end
- end
-end
diff --git a/ee/app/graphql/types/audit_events/amazon_s3_configuration_type.rb b/ee/app/graphql/types/audit_events/amazon_s3_configuration_type.rb
deleted file mode 100644
index 603097a51126b18a2afe045c4533b8773903c98f..0000000000000000000000000000000000000000
--- a/ee/app/graphql/types/audit_events/amazon_s3_configuration_type.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- module AuditEvents
- class AmazonS3ConfigurationType < ::Types::BaseObject
- graphql_name 'AmazonS3ConfigurationType'
- description 'Stores Amazon S3 configurations for audit event streaming.'
- authorize :admin_external_audit_events
-
- implements AmazonS3ConfigurationInterface
-
- field :group, ::Types::GroupType,
- null: false,
- description: 'Group the configuration belongs to.'
- end
- end
-end
diff --git a/ee/app/graphql/types/audit_events/external_audit_event_destination_interface.rb b/ee/app/graphql/types/audit_events/external_audit_event_destination_interface.rb
deleted file mode 100644
index 72aca40450be842b48169730e3281059cce02d95..0000000000000000000000000000000000000000
--- a/ee/app/graphql/types/audit_events/external_audit_event_destination_interface.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- module AuditEvents
- module ExternalAuditEventDestinationInterface
- include Types::BaseInterface
-
- field :id, GraphQL::Types::ID,
- null: false,
- description: 'ID of the destination.'
-
- field :name, GraphQL::Types::String,
- null: false,
- description: 'Name of the external destination to send audit events to.'
-
- field :destination_url, GraphQL::Types::String,
- null: false,
- description: 'External destination to send audit events to.'
-
- field :verification_token, GraphQL::Types::String,
- null: false,
- description: 'Verification token to validate source of event.'
-
- field :event_type_filters, [GraphQL::Types::String],
- null: false,
- description: 'List of event type filters added for streaming.'
-
- field :active, GraphQL::Types::Boolean,
- null: false,
- description: 'Active status of the destination.'
- end
- end
-end
diff --git a/ee/app/graphql/types/audit_events/external_audit_event_destination_type.rb b/ee/app/graphql/types/audit_events/external_audit_event_destination_type.rb
deleted file mode 100644
index 5ff40943d925b558f59c6a1cbc067fe8aea97645..0000000000000000000000000000000000000000
--- a/ee/app/graphql/types/audit_events/external_audit_event_destination_type.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- module AuditEvents
- class ExternalAuditEventDestinationType < ::Types::BaseObject
- graphql_name 'ExternalAuditEventDestination'
- description 'Represents an external resource to send audit events to'
- authorize :admin_external_audit_events
-
- implements ExternalAuditEventDestinationInterface
-
- field :group, ::Types::GroupType,
- null: false,
- description: 'Group the destination belongs to.'
-
- field :headers, ::Types::AuditEvents::Streaming::HeaderType.connection_type,
- null: false,
- description: 'List of additional HTTP headers sent with each event.'
-
- field :namespace_filter, ::Types::AuditEvents::Streaming::HTTP::NamespaceFilterType,
- null: true,
- description: 'List of subgroup or project filters for the destination.'
- end
- end
-end
diff --git a/ee/app/graphql/types/audit_events/google_cloud_logging_configuration_interface.rb b/ee/app/graphql/types/audit_events/google_cloud_logging_configuration_interface.rb
deleted file mode 100644
index 66c160c06039d8b6dff587c48ad1adda6b1259b3..0000000000000000000000000000000000000000
--- a/ee/app/graphql/types/audit_events/google_cloud_logging_configuration_interface.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- module AuditEvents
- module GoogleCloudLoggingConfigurationInterface
- include Types::BaseInterface
-
- field :id, GraphQL::Types::ID,
- null: false,
- description: 'ID of the configuration.'
-
- field :google_project_id_name, GraphQL::Types::String,
- null: false,
- description: 'Google project ID.'
-
- field :client_email, GraphQL::Types::String,
- null: false,
- description: 'Client email.'
-
- field :log_id_name, GraphQL::Types::String,
- null: false,
- description: 'Log ID.'
-
- field :name, GraphQL::Types::String,
- null: false,
- description: 'Name of the external destination to send audit events to.'
-
- field :active, GraphQL::Types::Boolean,
- null: false,
- description: 'Active status of the destination.'
- end
- end
-end
diff --git a/ee/app/graphql/types/audit_events/google_cloud_logging_configuration_type.rb b/ee/app/graphql/types/audit_events/google_cloud_logging_configuration_type.rb
deleted file mode 100644
index fb6fef1426bdfa1f9ab6c01a7188a4ec357d6b2a..0000000000000000000000000000000000000000
--- a/ee/app/graphql/types/audit_events/google_cloud_logging_configuration_type.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- module AuditEvents
- class GoogleCloudLoggingConfigurationType < ::Types::BaseObject
- graphql_name 'GoogleCloudLoggingConfigurationType'
- description 'Stores Google Cloud Logging configurations associated with IAM service accounts,' \
- 'used for generating access tokens.'
- authorize :admin_external_audit_events
-
- implements GoogleCloudLoggingConfigurationInterface
-
- field :group, ::Types::GroupType,
- null: false,
- description: 'Group the configuration belongs to.'
- end
- end
-end
diff --git a/ee/app/graphql/types/audit_events/instance/amazon_s3_configuration_type.rb b/ee/app/graphql/types/audit_events/instance/amazon_s3_configuration_type.rb
deleted file mode 100644
index 517af4c482df0fa23eada417e2b22d9a28acfc4d..0000000000000000000000000000000000000000
--- a/ee/app/graphql/types/audit_events/instance/amazon_s3_configuration_type.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- module AuditEvents
- module Instance
- class AmazonS3ConfigurationType < ::Types::BaseObject
- graphql_name 'InstanceAmazonS3ConfigurationType'
- description 'Stores instance level Amazon S3 configurations for audit event streaming.'
- authorize :admin_instance_external_audit_events
-
- implements AmazonS3ConfigurationInterface
- end
- end
- end
-end
diff --git a/ee/app/graphql/types/audit_events/instance/google_cloud_logging_configuration_type.rb b/ee/app/graphql/types/audit_events/instance/google_cloud_logging_configuration_type.rb
deleted file mode 100644
index a620065b2216ad7db505b3e0e49b5f3e6b636d34..0000000000000000000000000000000000000000
--- a/ee/app/graphql/types/audit_events/instance/google_cloud_logging_configuration_type.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- module AuditEvents
- module Instance
- class GoogleCloudLoggingConfigurationType < ::Types::BaseObject
- graphql_name 'InstanceGoogleCloudLoggingConfigurationType'
- description 'Stores instance level Google Cloud Logging configurations associated with IAM service accounts,' \
- 'used for generating access tokens.'
- authorize :admin_instance_external_audit_events
-
- implements GoogleCloudLoggingConfigurationInterface
- end
- end
- end
-end
diff --git a/ee/app/graphql/types/audit_events/instance_external_audit_event_destination_type.rb b/ee/app/graphql/types/audit_events/instance_external_audit_event_destination_type.rb
deleted file mode 100644
index a2e1239fe3997df4e8ce611dbbb2d7637ef67cca..0000000000000000000000000000000000000000
--- a/ee/app/graphql/types/audit_events/instance_external_audit_event_destination_type.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- module AuditEvents
- class InstanceExternalAuditEventDestinationType < ::Types::BaseObject
- graphql_name 'InstanceExternalAuditEventDestination'
- description 'Represents an external resource to send instance audit events to'
-
- authorize :admin_instance_external_audit_events
-
- implements ExternalAuditEventDestinationInterface
-
- field :headers, ::Types::AuditEvents::Streaming::InstanceHeaderType.connection_type,
- null: false,
- description: 'List of additional HTTP headers sent with each event.'
- end
- end
-end
diff --git a/ee/app/graphql/types/audit_events/streaming/base_header_interface.rb b/ee/app/graphql/types/audit_events/streaming/base_header_interface.rb
deleted file mode 100644
index 433f46a6b7f6737d715e412f2a20010202858a55..0000000000000000000000000000000000000000
--- a/ee/app/graphql/types/audit_events/streaming/base_header_interface.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- module AuditEvents
- module Streaming
- module BaseHeaderInterface
- include Types::BaseInterface
-
- field :id, GraphQL::Types::ID,
- null: false,
- description: 'ID of the header.'
-
- field :key, GraphQL::Types::String,
- null: false,
- description: 'Key of the header.'
-
- field :value, GraphQL::Types::String,
- null: false,
- description: 'Value of the header.'
-
- field :active, GraphQL::Types::Boolean,
- null: false,
- description: 'Header is active or not.'
- end
- end
- end
-end
diff --git a/ee/app/graphql/types/audit_events/streaming/header_type.rb b/ee/app/graphql/types/audit_events/streaming/header_type.rb
deleted file mode 100644
index db22ff012639fff2789d1e5f3587083807c8a5ab..0000000000000000000000000000000000000000
--- a/ee/app/graphql/types/audit_events/streaming/header_type.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# frozen_string_literal: true
-
-# Headers are only available through destinations
-# which are already authorized.
-module Types
- module AuditEvents
- module Streaming
- class HeaderType < ::Types::BaseObject
- graphql_name 'AuditEventStreamingHeader'
-
- description 'Represents a HTTP header key/value that belongs to an audit streaming destination.'
-
- authorize :admin_external_audit_events
-
- implements BaseHeaderInterface
- end
- end
- end
-end
diff --git a/ee/app/graphql/types/audit_events/streaming/http/namespace_filter_type.rb b/ee/app/graphql/types/audit_events/streaming/http/namespace_filter_type.rb
deleted file mode 100644
index d266c26e08d974f1fec0b78bd157e63b73a64021..0000000000000000000000000000000000000000
--- a/ee/app/graphql/types/audit_events/streaming/http/namespace_filter_type.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- module AuditEvents
- module Streaming
- module HTTP
- class NamespaceFilterType < ::Types::BaseObject
- graphql_name 'AuditEventStreamingHTTPNamespaceFilter'
-
- description 'Represents a subgroup or project filter that belongs to ' \
- 'an external audit event streaming destination.'
-
- authorize :admin_external_audit_events
-
- field :id, GraphQL::Types::ID,
- null: false,
- description: 'ID of the filter.'
-
- field :namespace, ::Types::NamespaceType,
- null: false,
- description: 'Group or project namespace the filter belongs to.'
-
- field :external_audit_event_destination, ::Types::AuditEvents::ExternalAuditEventDestinationType,
- null: false,
- description: 'Destination to which the filter belongs.'
- end
- end
- end
- end
-end
diff --git a/ee/app/graphql/types/audit_events/streaming/instance_header_type.rb b/ee/app/graphql/types/audit_events/streaming/instance_header_type.rb
deleted file mode 100644
index c6b1e23d3cc9105ead1b1858e51a108e21a3765b..0000000000000000000000000000000000000000
--- a/ee/app/graphql/types/audit_events/streaming/instance_header_type.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- module AuditEvents
- module Streaming
- class InstanceHeaderType < ::Types::BaseObject
- graphql_name 'AuditEventsStreamingInstanceHeader'
-
- description 'Represents a HTTP header key/value that belongs to an instance level audit streaming destination.'
-
- authorize :admin_instance_external_audit_events
-
- implements BaseHeaderInterface
- end
- end
- end
-end
diff --git a/ee/config/feature_flags/beta/use_consolidated_audit_event_stream_dest_api.yml b/ee/config/feature_flags/beta/use_consolidated_audit_event_stream_dest_api.yml
deleted file mode 100644
index d0d98253901791a5ba31f0564c575767572a6e68..0000000000000000000000000000000000000000
--- a/ee/config/feature_flags/beta/use_consolidated_audit_event_stream_dest_api.yml
+++ /dev/null
@@ -1,9 +0,0 @@
----
-name: use_consolidated_audit_event_stream_dest_api
-feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/442447
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/183848
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/523880
-milestone: '17.10'
-group: group::compliance
-type: beta
-default_enabled: true
diff --git a/ee/spec/frontend/audit_events/components/audit_events_stream_spec.js b/ee/spec/frontend/audit_events/components/audit_events_stream_spec.js
index 50a3199ca4aaf1e600a95fd7f0086db391f5db4d..27d73b3aef3fbec9a81b4de9cea2c036adf0309d 100644
--- a/ee/spec/frontend/audit_events/components/audit_events_stream_spec.js
+++ b/ee/spec/frontend/audit_events/components/audit_events_stream_spec.js
@@ -8,12 +8,6 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import groupStreamingDestinationsQuery from 'ee/audit_events/graphql/queries/get_group_streaming_destinations.query.graphql';
import instanceStreamingDestinationsQuery from 'ee/audit_events/graphql/queries/get_instance_streaming_destinations.query.graphql';
-import externalDestinationsQuery from 'ee/audit_events/graphql/queries/get_external_destinations.query.graphql';
-import instanceExternalDestinationsQuery from 'ee/audit_events/graphql/queries/get_instance_external_destinations.query.graphql';
-import gcpLoggingDestinationsQuery from 'ee/audit_events/graphql/queries/get_google_cloud_logging_destinations.query.graphql';
-import instanceGcpLoggingDestinationsQuery from 'ee/audit_events/graphql/queries/get_instance_google_cloud_logging_destinations.query.graphql';
-import amazonS3DestinationsQuery from 'ee/audit_events/graphql/queries/get_amazon_s3_destinations.query.graphql';
-import instanceAmazonS3DestinationsQuery from 'ee/audit_events/graphql/queries/get_instance_amazon_s3_destinations.query.graphql';
import {
AUDIT_STREAMS_NETWORK_ERRORS,
@@ -22,24 +16,9 @@ import {
} from 'ee/audit_events/constants';
import AuditEventsStream from 'ee/audit_events/components/audit_events_stream.vue';
import StreamDestinationEditor from 'ee/audit_events/components/stream/stream_destination_editor.vue';
-import StreamHttpDestinationEditor from 'ee/audit_events/components/stream/stream_http_destination_editor.vue';
-import StreamGcpLoggingDestinationEditor from 'ee/audit_events/components/stream/stream_gcp_logging_destination_editor.vue';
-import StreamAmazonS3DestinationEditor from 'ee/audit_events/components/stream/stream_amazon_s3_destination_editor.vue';
import StreamItem from 'ee/audit_events/components/stream/stream_item.vue';
import StreamEmptyState from 'ee/audit_events/components/stream/stream_empty_state.vue';
-import {
- mockExternalDestinations,
- groupPath,
- destinationDataPopulator,
- mockInstanceExternalDestinations,
- instanceGroupPath,
- instanceDestinationDataPopulator,
- gcpLoggingDataPopulator,
- mockGcpLoggingDestinations,
- mockInstanceGcpLoggingDestinations,
- mockAmazonS3Destinations,
- mockInstanceAmazonS3Destinations,
-} from '../mock_data';
+import { groupPath, instanceGroupPath } from '../mock_data';
import {
mockAllAPIDestinations,
groupStreamingDestinationDataPopulator,
@@ -60,21 +39,8 @@ describe('AuditEventsStream', () => {
const instanceStreamingDestinationsQuerySpy = jest
.fn()
.mockResolvedValue(instanceStreamingDestinationDataPopulator(mockAllAPIDestinations));
- const externalDestinationsQuerySpy = jest
- .fn()
- .mockResolvedValue(destinationDataPopulator(mockExternalDestinations));
- const externalGcpLoggingQuerySpy = jest
- .fn()
- .mockResolvedValue(gcpLoggingDataPopulator(mockGcpLoggingDestinations));
- const externalAmazonS3QuerySpy = jest
- .fn()
- .mockResolvedValue(gcpLoggingDataPopulator(mockAmazonS3Destinations));
-
- const defaultProvide = {
- glFeatures: { useConsolidatedAuditEventStreamDestApi: false },
- };
- const createComponent = ({ apolloProvider, provide = defaultProvide } = {}) => {
+ const createComponent = ({ apolloProvider, provide = {} } = {}) => {
wrapper = mountExtended(AuditEventsStream, {
provide: {
groupPath: providedGroupPath,
@@ -86,9 +52,6 @@ describe('AuditEventsStream', () => {
GlLoadingIcon: true,
StreamItem: true,
StreamDestinationEditor: true,
- StreamHttpDestinationEditor: true,
- StreamGcpLoggingDestinationEditor: true,
- StreamAmazonS3DestinationEditor: true,
StreamEmptyState: true,
},
});
@@ -99,23 +62,13 @@ describe('AuditEventsStream', () => {
const findDisclosureDropdownItem = (index) =>
wrapper.findAllComponents(GlDisclosureDropdownItem).at(index).find('button');
const findHttpDropdownItem = () => findDisclosureDropdownItem(0);
- const findGcpLoggingDropdownItem = () => findDisclosureDropdownItem(1);
- const findAmazonS3DropdownItem = () => findDisclosureDropdownItem(2);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findStreamDestinationEditor = () => wrapper.findComponent(StreamDestinationEditor);
- const findStreamHttpDestinationEditor = () => wrapper.findComponent(StreamHttpDestinationEditor);
- const findStreamGcpLoggingDestinationEditor = () =>
- wrapper.findComponent(StreamGcpLoggingDestinationEditor);
- const findStreamAmazonS3DestinationEditor = () =>
- wrapper.findComponent(StreamAmazonS3DestinationEditor);
const findStreamEmptyState = () => wrapper.findComponent(StreamEmptyState);
const findStreamItems = () => wrapper.findAllComponents(StreamItem);
afterEach(() => {
createAlert.mockClear();
- externalDestinationsQuerySpy.mockClear();
- externalGcpLoggingQuerySpy.mockClear();
- externalAmazonS3QuerySpy.mockClear();
});
describe('Group AuditEventsStream', () => {
@@ -123,68 +76,19 @@ describe('AuditEventsStream', () => {
it('should render the loading icon while waiting for data to be returned', () => {
const destinationQuerySpy = jest.fn();
const apolloProvider = createMockApollo([
- [externalDestinationsQuery, destinationQuerySpy],
- [gcpLoggingDestinationsQuery, destinationQuerySpy],
- [amazonS3DestinationsQuery, destinationQuerySpy],
+ [groupStreamingDestinationsQuery, destinationQuerySpy],
]);
createComponent({ apolloProvider });
expect(findLoadingIcon().exists()).toBe(true);
});
- it('should still render the loading icon while waiting for external destination data to be returned', async () => {
- const destinationQuerySpy = jest.fn().mockImplementation(() => {
- return new Promise(() => {});
- });
- const apolloProvider = createMockApollo([
- [externalDestinationsQuery, destinationQuerySpy],
- [gcpLoggingDestinationsQuery, externalGcpLoggingQuerySpy],
- [amazonS3DestinationsQuery, externalAmazonS3QuerySpy],
- ]);
- createComponent({ apolloProvider });
- await waitForPromises();
-
- expect(findLoadingIcon().exists()).toBe(true);
- });
-
- it('should still render the loading icon while waiting for gcp logging destination data to be returned', async () => {
- const destinationQuerySpy = jest.fn().mockImplementation(() => {
- return new Promise(() => {});
- });
- const apolloProvider = createMockApollo([
- [externalDestinationsQuery, externalDestinationsQuerySpy],
- [gcpLoggingDestinationsQuery, destinationQuerySpy],
- [amazonS3DestinationsQuery, externalAmazonS3QuerySpy],
- ]);
- createComponent({ apolloProvider });
- await waitForPromises();
-
- expect(findLoadingIcon().exists()).toBe(true);
- });
-
- it('should still render the loading icon while waiting for aws s3 destination data to be returned', async () => {
- const destinationQuerySpy = jest.fn().mockImplementation(() => {
- return new Promise(() => {});
- });
- const apolloProvider = createMockApollo([
- [externalDestinationsQuery, destinationQuerySpy],
- [gcpLoggingDestinationsQuery, destinationQuerySpy],
- [amazonS3DestinationsQuery, destinationQuerySpy],
- ]);
- createComponent({ apolloProvider });
- await waitForPromises();
-
- expect(findLoadingIcon().exists()).toBe(true);
- });
-
it('should render empty state when no data is returned', async () => {
- const destinationQuerySpy = jest.fn().mockResolvedValue(destinationDataPopulator([]));
- const gcpLoggingQuerySpy = jest.fn().mockResolvedValue(gcpLoggingDataPopulator([]));
- const amazonS3QuerySpy = jest.fn().mockResolvedValue(gcpLoggingDataPopulator([]));
+ const destinationQuerySpy = jest
+ .fn()
+ .mockResolvedValue(groupStreamingDestinationDataPopulator([]));
const apolloProvider = createMockApollo([
- [externalDestinationsQuery, destinationQuerySpy],
- [gcpLoggingDestinationsQuery, gcpLoggingQuerySpy],
- [amazonS3DestinationsQuery, amazonS3QuerySpy],
+ [groupStreamingDestinationsQuery, destinationQuerySpy],
]);
createComponent({ apolloProvider });
await waitForPromises();
@@ -195,7 +99,9 @@ describe('AuditEventsStream', () => {
it('should report error when server error occurred', async () => {
const destinationQuerySpy = jest.fn().mockRejectedValue({});
- const apolloProvider = createMockApollo([[externalDestinationsQuery, destinationQuerySpy]]);
+ const apolloProvider = createMockApollo([
+ [groupStreamingDestinationsQuery, destinationQuerySpy],
+ ]);
createComponent({ apolloProvider });
await waitForPromises();
@@ -209,86 +115,35 @@ describe('AuditEventsStream', () => {
describe('when edit mode entered', () => {
beforeEach(() => {
const apolloProvider = createMockApollo([
- [externalDestinationsQuery, externalDestinationsQuerySpy],
- [gcpLoggingDestinationsQuery, externalGcpLoggingQuerySpy],
+ [groupStreamingDestinationsQuery, streamingDestinationsQuerySpy],
]);
createComponent({ apolloProvider });
return waitForPromises();
});
- it('shows http destination editor', async () => {
+ it('shows destination editor when entering edit mode', async () => {
expect(findLoadingIcon().exists()).toBe(false);
- expect(findStreamHttpDestinationEditor().exists()).toBe(false);
+ expect(findStreamDestinationEditor().exists()).toBe(false);
expect(findAddDestinationButton().props('toggleText')).toBe('Add streaming destination');
await findHttpDropdownItem().trigger('click');
- expect(findStreamHttpDestinationEditor().exists()).toBe(true);
+ expect(findStreamDestinationEditor().exists()).toBe(true);
});
- it('exits edit mode when an external http destination is added', async () => {
+ it('exits edit mode when a destination is added', async () => {
expect(findLoadingIcon().exists()).toBe(false);
- expect(findStreamHttpDestinationEditor().exists()).toBe(false);
+ expect(findStreamDestinationEditor().exists()).toBe(false);
await findHttpDropdownItem().trigger('click');
- const streamHttpDestinationEditorComponent = findStreamHttpDestinationEditor();
+ const streamDestinationEditorComponent = findStreamDestinationEditor();
- expect(streamHttpDestinationEditorComponent.exists()).toBe(true);
+ expect(streamDestinationEditorComponent.exists()).toBe(true);
- streamHttpDestinationEditorComponent.vm.$emit('added');
- await waitForPromises();
-
- expect(findSuccessMessage().text()).toBe(ADD_STREAM_MESSAGE);
- });
-
- it('shows gcp logging editor', async () => {
- expect(findLoadingIcon().exists()).toBe(false);
- expect(findStreamGcpLoggingDestinationEditor().exists()).toBe(false);
-
- expect(findAddDestinationButton().props('toggleText')).toBe('Add streaming destination');
-
- await findGcpLoggingDropdownItem().trigger('click');
-
- expect(findStreamGcpLoggingDestinationEditor().exists()).toBe(true);
- });
-
- it('exits edit mode when an external gcp logging destination is added', async () => {
- expect(findLoadingIcon().exists()).toBe(false);
- expect(findStreamGcpLoggingDestinationEditor().exists()).toBe(false);
-
- await findGcpLoggingDropdownItem().trigger('click');
-
- expect(findStreamGcpLoggingDestinationEditor().exists()).toBe(true);
-
- findStreamGcpLoggingDestinationEditor().vm.$emit('added');
- await waitForPromises();
-
- expect(findSuccessMessage().text()).toBe(ADD_STREAM_MESSAGE);
- });
-
- it('shows amazon s3 editor', async () => {
- expect(findLoadingIcon().exists()).toBe(false);
- expect(findStreamAmazonS3DestinationEditor().exists()).toBe(false);
-
- expect(findAddDestinationButton().props('toggleText')).toBe('Add streaming destination');
-
- await findAmazonS3DropdownItem().trigger('click');
-
- expect(findStreamAmazonS3DestinationEditor().exists()).toBe(true);
- });
-
- it('exits edit mode when an external amazon s3 destination is added', async () => {
- expect(findLoadingIcon().exists()).toBe(false);
- expect(findStreamAmazonS3DestinationEditor().exists()).toBe(false);
-
- await findAmazonS3DropdownItem().trigger('click');
-
- expect(findStreamAmazonS3DestinationEditor().exists()).toBe(true);
-
- findStreamAmazonS3DestinationEditor().vm.$emit('added');
+ streamDestinationEditorComponent.vm.$emit('added');
await waitForPromises();
expect(findSuccessMessage().text()).toBe(ADD_STREAM_MESSAGE);
@@ -297,14 +152,14 @@ describe('AuditEventsStream', () => {
it('clears the success message if an error occurs afterwards', async () => {
await findHttpDropdownItem().trigger('click');
- findStreamHttpDestinationEditor().vm.$emit('added');
+ findStreamDestinationEditor().vm.$emit('added');
await waitForPromises();
expect(findSuccessMessage().text()).toBe(ADD_STREAM_MESSAGE);
await findHttpDropdownItem().trigger('click');
- await findStreamHttpDestinationEditor().vm.$emit('error');
+ await findStreamDestinationEditor().vm.$emit('error');
expect(findSuccessMessage().exists()).toBe(false);
});
@@ -313,7 +168,7 @@ describe('AuditEventsStream', () => {
describe('Streaming items', () => {
beforeEach(() => {
const apolloProvider = createMockApollo([
- [externalDestinationsQuery, externalDestinationsQuerySpy],
+ [groupStreamingDestinationsQuery, streamingDestinationsQuerySpy],
]);
createComponent({ apolloProvider });
@@ -321,17 +176,46 @@ describe('AuditEventsStream', () => {
});
it('shows the items', () => {
- expect(findStreamItems()).toHaveLength(2);
+ expect(findStreamItems()).toHaveLength(mockAllAPIDestinations.length);
+
+ findStreamItems().wrappers.forEach((streamItem, index) => {
+ expect(streamItem.props('item').id).toBe(mockAllAPIDestinations[index].id);
+ });
+ });
+
+ it('captures an error when the destination category is not recognized', async () => {
+ const unknownAPIDestination = {
+ __typename: 'GroupAuditEventStreamingDestination',
+ id: 'mock-streaming-destination-1',
+ name: 'Unknown Destination 1',
+ category: 'something_else',
+ secretToken: '',
+ config: {},
+ eventTypeFilters: [],
+ namespaceFilters: [],
+ active: true,
+ };
+ const streamingDestinationsQueryUnknownCategory = jest
+ .fn()
+ .mockResolvedValue(groupStreamingDestinationDataPopulator([unknownAPIDestination]));
+ const apolloProvider = createMockApollo([
+ [groupStreamingDestinationsQuery, streamingDestinationsQueryUnknownCategory],
+ ]);
+ createComponent({
+ apolloProvider,
+ });
+ await waitForPromises();
- expect(findStreamItems().at(0).props('item')).toStrictEqual(mockExternalDestinations[0]);
- expect(findStreamItems().at(1).props('item')).toStrictEqual(mockExternalDestinations[1]);
+ expect(Sentry.captureException).toHaveBeenCalledWith(
+ new Error('Unknown destination category: something_else'),
+ );
});
it('updates list when destination is removed', async () => {
await waitForPromises();
expect(findLoadingIcon().exists()).toBe(false);
- expect(externalDestinationsQuerySpy).toHaveBeenCalledTimes(1);
+ expect(streamingDestinationsQuerySpy).toHaveBeenCalledTimes(1);
const currentLength = findStreamItems().length;
findStreamItems().at(0).vm.$emit('deleted');
@@ -339,85 +223,6 @@ describe('AuditEventsStream', () => {
expect(findStreamItems()).toHaveLength(currentLength - 1);
expect(findSuccessMessage().text()).toBe(DELETE_STREAM_MESSAGE);
});
-
- describe('when useConsolidatedAuditEventStreamDestApi is enabled', () => {
- beforeEach(() => {
- const apolloProvider = createMockApollo([
- [groupStreamingDestinationsQuery, streamingDestinationsQuerySpy],
- ]);
- createComponent({
- apolloProvider,
- provide: {
- glFeatures: { useConsolidatedAuditEventStreamDestApi: true },
- },
- });
-
- return waitForPromises();
- });
-
- it('shows the items', () => {
- expect(findStreamItems()).toHaveLength(mockAllAPIDestinations.length);
-
- findStreamItems().wrappers.forEach((streamItem, index) => {
- expect(streamItem.props('item').id).toBe(mockAllAPIDestinations[index].id);
- });
- });
-
- it('captures an error when the destination category is not recognized', async () => {
- const unknownAPIDestination = {
- __typename: 'GroupAuditEventStreamingDestination',
- id: 'mock-streaming-destination-1',
- name: 'Unknown Destination 1',
- category: 'something_else',
- secretToken: '',
- config: {},
- eventTypeFilters: [],
- namespaceFilters: [],
- active: true,
- };
- const streamingDestinationsQueryUnknownCategory = jest
- .fn()
- .mockResolvedValue(groupStreamingDestinationDataPopulator([unknownAPIDestination]));
- const apolloProvider = createMockApollo([
- [groupStreamingDestinationsQuery, streamingDestinationsQueryUnknownCategory],
- ]);
- createComponent({
- apolloProvider,
- provide: {
- glFeatures: { useConsolidatedAuditEventStreamDestApi: true },
- },
- });
- await waitForPromises();
-
- expect(Sentry.captureException).toHaveBeenCalledWith(
- new Error('Unknown destination category: something_else'),
- );
- });
-
- it('updates list when destination is removed', async () => {
- await waitForPromises();
-
- expect(findLoadingIcon().exists()).toBe(false);
- expect(streamingDestinationsQuerySpy).toHaveBeenCalledTimes(1);
-
- const currentLength = findStreamItems().length;
- findStreamItems().at(0).vm.$emit('deleted');
- await waitForPromises();
- expect(findStreamItems()).toHaveLength(currentLength - 1);
- expect(findSuccessMessage().text()).toBe(DELETE_STREAM_MESSAGE);
- });
-
- it('shows destination editor when entering edit mode', async () => {
- expect(findLoadingIcon().exists()).toBe(false);
- expect(findStreamDestinationEditor().exists()).toBe(false);
-
- expect(findAddDestinationButton().props('toggleText')).toBe('Add streaming destination');
-
- await findHttpDropdownItem().trigger('click');
-
- expect(findStreamDestinationEditor().exists()).toBe(true);
- });
- });
});
});
@@ -426,89 +231,27 @@ describe('AuditEventsStream', () => {
providedGroupPath = instanceGroupPath;
});
- const externalInstanceDestinationsQuerySpy = jest
- .fn()
- .mockResolvedValue(instanceDestinationDataPopulator(mockInstanceExternalDestinations));
- const externalInstanceGcpLoggingQuerySpy = jest
- .fn()
- .mockResolvedValue(gcpLoggingDataPopulator(mockInstanceGcpLoggingDestinations));
- const externalInstanceAmazonS3QuerySpy = jest
- .fn()
- .mockResolvedValue(gcpLoggingDataPopulator(mockInstanceAmazonS3Destinations));
-
afterEach(() => {
createAlert.mockClear();
- externalInstanceDestinationsQuerySpy.mockClear();
- externalInstanceGcpLoggingQuerySpy.mockClear();
- externalInstanceAmazonS3QuerySpy.mockClear();
});
describe('when initialized', () => {
it('should render the loading icon while waiting for data to be returned', () => {
const destinationQuerySpy = jest.fn();
const apolloProvider = createMockApollo([
- [instanceExternalDestinationsQuery, destinationQuerySpy],
- [instanceGcpLoggingDestinationsQuery, destinationQuerySpy],
- [instanceAmazonS3DestinationsQuery, destinationQuerySpy],
+ [instanceStreamingDestinationsQuery, destinationQuerySpy],
]);
createComponent({ apolloProvider });
expect(findLoadingIcon().exists()).toBe(true);
});
- it('should still render the loading icon while waiting for external destination data to be returned', async () => {
- const destinationQuerySpy = jest.fn().mockImplementation(() => {
- return new Promise(() => {});
- });
- const apolloProvider = createMockApollo([
- [instanceExternalDestinationsQuery, destinationQuerySpy],
- [instanceGcpLoggingDestinationsQuery, externalInstanceGcpLoggingQuerySpy],
- [instanceAmazonS3DestinationsQuery, externalInstanceAmazonS3QuerySpy],
- ]);
- createComponent({ apolloProvider });
- await waitForPromises();
-
- expect(findLoadingIcon().exists()).toBe(true);
- });
-
- it('should still render the loading icon while waiting for gcp logging destination data to be returned', async () => {
- const destinationQuerySpy = jest.fn().mockImplementation(() => {
- return new Promise(() => {});
- });
- const apolloProvider = createMockApollo([
- [instanceExternalDestinationsQuery, externalInstanceDestinationsQuerySpy],
- [instanceGcpLoggingDestinationsQuery, destinationQuerySpy],
- [instanceAmazonS3DestinationsQuery, externalInstanceAmazonS3QuerySpy],
- ]);
- createComponent({ apolloProvider });
- await waitForPromises();
-
- expect(findLoadingIcon().exists()).toBe(true);
- });
-
- it('should still render the loading icon while waiting for aws s3 destination data to be returned', async () => {
- const destinationQuerySpy = jest.fn().mockImplementation(() => {
- return new Promise(() => {});
- });
- const apolloProvider = createMockApollo([
- [instanceExternalDestinationsQuery, destinationQuerySpy],
- [instanceGcpLoggingDestinationsQuery, externalInstanceGcpLoggingQuerySpy],
- [instanceAmazonS3DestinationsQuery, destinationQuerySpy],
- ]);
- createComponent({ apolloProvider });
- await waitForPromises();
-
- expect(findLoadingIcon().exists()).toBe(true);
- });
-
it('should render empty state when no data is returned', async () => {
- const destinationQuerySpy = jest.fn().mockResolvedValue(destinationDataPopulator([]));
- const gcpLoggingQuerySpy = jest.fn().mockResolvedValue(gcpLoggingDataPopulator([]));
- const amazonS3QuerySpy = jest.fn().mockResolvedValue(gcpLoggingDataPopulator([]));
+ const destinationQuerySpy = jest
+ .fn()
+ .mockResolvedValue(instanceStreamingDestinationDataPopulator([]));
const apolloProvider = createMockApollo([
- [instanceExternalDestinationsQuery, destinationQuerySpy],
- [instanceGcpLoggingDestinationsQuery, gcpLoggingQuerySpy],
- [instanceAmazonS3DestinationsQuery, amazonS3QuerySpy],
+ [instanceStreamingDestinationsQuery, destinationQuerySpy],
]);
createComponent({ apolloProvider });
await waitForPromises();
@@ -520,9 +263,7 @@ describe('AuditEventsStream', () => {
it('should report error when server error occurred', async () => {
const instanceDestinationQuerySpy = jest.fn().mockRejectedValue({});
const apolloProvider = createMockApollo([
- [instanceExternalDestinationsQuery, instanceDestinationQuerySpy],
- [instanceGcpLoggingDestinationsQuery, instanceDestinationQuerySpy],
- [instanceAmazonS3DestinationsQuery, instanceDestinationQuerySpy],
+ [instanceStreamingDestinationsQuery, instanceDestinationQuerySpy],
]);
createComponent({ apolloProvider });
await waitForPromises();
@@ -537,7 +278,7 @@ describe('AuditEventsStream', () => {
describe('when edit mode entered', () => {
beforeEach(() => {
const apolloProvider = createMockApollo([
- [instanceExternalDestinationsQuery, externalInstanceDestinationsQuerySpy],
+ [instanceStreamingDestinationsQuery, instanceStreamingDestinationsQuerySpy],
]);
createComponent({ apolloProvider });
@@ -548,64 +289,24 @@ describe('AuditEventsStream', () => {
expect(findLoadingIcon().exists()).toBe(false);
});
- it('shows http destination editor', async () => {
- expect(findStreamHttpDestinationEditor().exists()).toBe(false);
-
- await findHttpDropdownItem().trigger('click');
-
- expect(findStreamHttpDestinationEditor().exists()).toBe(true);
- });
-
- it('exits edit mode when an http external destination is added', async () => {
- expect(findStreamHttpDestinationEditor().exists()).toBe(false);
-
- await findHttpDropdownItem().trigger('click');
-
- expect(findStreamHttpDestinationEditor().exists()).toBe(true);
-
- findStreamHttpDestinationEditor().vm.$emit('added');
- await waitForPromises();
-
- expect(findSuccessMessage().text()).toBe(ADD_STREAM_MESSAGE);
- });
-
- it('shows gcp logging editor', async () => {
- expect(findStreamGcpLoggingDestinationEditor().exists()).toBe(false);
+ it('shows destination editor when entering edit mode', async () => {
+ expect(findStreamDestinationEditor().exists()).toBe(false);
expect(findAddDestinationButton().props('toggleText')).toBe('Add streaming destination');
- await findGcpLoggingDropdownItem().trigger('click');
-
- expect(findStreamGcpLoggingDestinationEditor().exists()).toBe(true);
- });
-
- it('exits edit mode when an external gcp logging destination is added', async () => {
- expect(findStreamGcpLoggingDestinationEditor().exists()).toBe(false);
-
- await findGcpLoggingDropdownItem().trigger('click');
-
- expect(findStreamGcpLoggingDestinationEditor().exists()).toBe(true);
-
- findStreamGcpLoggingDestinationEditor().vm.$emit('added');
- await waitForPromises();
-
- expect(findSuccessMessage().text()).toBe(ADD_STREAM_MESSAGE);
- });
-
- it('shows amazon s3 editor', () => {
- expect(findStreamAmazonS3DestinationEditor().exists()).toBe(false);
+ await findHttpDropdownItem().trigger('click');
- expect(findAddDestinationButton().props('toggleText')).toBe('Add streaming destination');
+ expect(findStreamDestinationEditor().exists()).toBe(true);
});
- it('exits edit mode when an external amazon s3 destination is added', async () => {
- expect(findStreamAmazonS3DestinationEditor().exists()).toBe(false);
+ it('exits edit mode when a destination is added', async () => {
+ expect(findStreamDestinationEditor().exists()).toBe(false);
- await findAmazonS3DropdownItem().trigger('click');
+ await findHttpDropdownItem().trigger('click');
- expect(findStreamAmazonS3DestinationEditor().exists()).toBe(true);
+ expect(findStreamDestinationEditor().exists()).toBe(true);
- findStreamAmazonS3DestinationEditor().vm.$emit('added');
+ findStreamDestinationEditor().vm.$emit('added');
await waitForPromises();
expect(findSuccessMessage().text()).toBe(ADD_STREAM_MESSAGE);
@@ -614,14 +315,14 @@ describe('AuditEventsStream', () => {
it('clears the success message if an error occurs afterwards', async () => {
await findHttpDropdownItem().trigger('click');
- findStreamHttpDestinationEditor().vm.$emit('added');
+ findStreamDestinationEditor().vm.$emit('added');
await waitForPromises();
expect(findSuccessMessage().text()).toBe(ADD_STREAM_MESSAGE);
await findHttpDropdownItem().trigger('click');
- await findStreamHttpDestinationEditor().vm.$emit('error');
+ await findStreamDestinationEditor().vm.$emit('error');
expect(findSuccessMessage().exists()).toBe(false);
});
@@ -630,7 +331,7 @@ describe('AuditEventsStream', () => {
describe('Streaming items', () => {
beforeEach(() => {
const apolloProvider = createMockApollo([
- [instanceExternalDestinationsQuery, externalInstanceDestinationsQuerySpy],
+ [instanceStreamingDestinationsQuery, instanceStreamingDestinationsQuerySpy],
]);
createComponent({ apolloProvider });
@@ -638,21 +339,18 @@ describe('AuditEventsStream', () => {
});
it('shows the items', () => {
- expect(findStreamItems()).toHaveLength(2);
+ expect(findStreamItems()).toHaveLength(mockAllAPIDestinations.length);
- expect(findStreamItems().at(0).props('item')).toStrictEqual(
- mockInstanceExternalDestinations[0],
- );
- expect(findStreamItems().at(1).props('item')).toStrictEqual(
- mockInstanceExternalDestinations[1],
- );
+ findStreamItems().wrappers.forEach((streamItem, index) => {
+ expect(streamItem.props('item').id).toBe(mockAllAPIDestinations[index].id);
+ });
});
it('updates list when destination is removed', async () => {
await waitForPromises();
expect(findLoadingIcon().exists()).toBe(false);
- expect(externalInstanceDestinationsQuerySpy).toHaveBeenCalledTimes(1);
+ expect(instanceStreamingDestinationsQuerySpy).toHaveBeenCalledTimes(1);
const currentLength = findStreamItems().length;
findStreamItems().at(0).vm.$emit('deleted');
@@ -660,43 +358,6 @@ describe('AuditEventsStream', () => {
expect(findStreamItems()).toHaveLength(currentLength - 1);
expect(findSuccessMessage().text()).toBe(DELETE_STREAM_MESSAGE);
});
-
- describe('when useConsolidatedAuditEventStreamDestApi is enabled', () => {
- beforeEach(() => {
- const apolloProvider = createMockApollo([
- [instanceStreamingDestinationsQuery, instanceStreamingDestinationsQuerySpy],
- ]);
- createComponent({
- apolloProvider,
- provide: {
- glFeatures: { useConsolidatedAuditEventStreamDestApi: true },
- },
- });
-
- return waitForPromises();
- });
-
- it('shows the items', () => {
- expect(findStreamItems()).toHaveLength(mockAllAPIDestinations.length);
-
- findStreamItems().wrappers.forEach((streamItem, index) => {
- expect(streamItem.props('item').id).toBe(mockAllAPIDestinations[index].id);
- });
- });
-
- it('updates list when destination is removed', async () => {
- await waitForPromises();
-
- expect(findLoadingIcon().exists()).toBe(false);
- expect(instanceStreamingDestinationsQuerySpy).toHaveBeenCalledTimes(1);
-
- const currentLength = findStreamItems().length;
- findStreamItems().at(0).vm.$emit('deleted');
- await waitForPromises();
- expect(findStreamItems()).toHaveLength(currentLength - 1);
- expect(findSuccessMessage().text()).toBe(DELETE_STREAM_MESSAGE);
- });
- });
});
});
});
diff --git a/ee/spec/frontend/audit_events/components/stream/__snapshots__/stream_item_spec.js.snap b/ee/spec/frontend/audit_events/components/stream/__snapshots__/stream_item_spec.js.snap
index 0589dd33d2a1ff3fb3f995d263dcff26f473d974..0e4b2cbd835388f2f3353db6373b7c8d045167c6 100644
--- a/ee/spec/frontend/audit_events/components/stream/__snapshots__/stream_item_spec.js.snap
+++ b/ee/spec/frontend/audit_events/components/stream/__snapshots__/stream_item_spec.js.snap
@@ -1,23 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`StreamItem Group http StreamItem when an item has event filters renders a popover 1`] = `
-
-`;
-
-exports[`StreamItem Group http StreamItem when an item has namespace filters renders a popover 1`] = `
+exports[`StreamItem Group StreamItem when an item has event filters renders a popover 1`] = `
{
- let wrapper;
- let groupPathProvide = groupPath;
-
- const createComponent = ({
- mountFn = mountExtended,
- props = {},
- provide = {},
- apolloHandlers = [
- [
- amazonS3ConfigurationCreate,
- jest.fn().mockResolvedValue(amazonS3DestinationCreateMutationPopulator()),
- ],
- ],
- } = {}) => {
- const mockApollo = createMockApollo(apolloHandlers);
- wrapper = mountFn(StreamAmazonS3DestinationEditor, {
- attachTo: document.body,
- provide: {
- groupPath: groupPathProvide,
- ...provide,
- },
- propsData: {
- ...props,
- },
- apolloProvider: mockApollo,
- });
- };
-
- const findWarningMessage = () => wrapper.findByTestId('data-warning');
- const findAlertErrors = () => wrapper.findAllByTestId('alert-errors');
- const findDestinationForm = () => wrapper.findComponent(GlForm);
- const findSubmitStreamBtn = () => wrapper.findByTestId('stream-destination-submit-button');
- const findCancelStreamBtn = () => wrapper.findByTestId('stream-destination-cancel-button');
- const findDeleteBtn = () => wrapper.findByTestId('stream-destination-delete-button');
- const findDeleteModal = () => wrapper.findComponent(StreamDeleteModal);
-
- const findNameFormGroup = () => wrapper.findByTestId('name-form-group');
- const findName = () => wrapper.findByTestId('name');
- const findAccessKeyXidFormGroup = () => wrapper.findByTestId('access-key-xid-form-group');
- const findAccessKeyXid = () => wrapper.findByTestId('access-key-xid');
- const findAwsRegionFormGroup = () => wrapper.findByTestId('aws-region-form-group');
- const findAwsRegion = () => wrapper.findByTestId('aws-region');
- const findBucketNameFormGroup = () => wrapper.findByTestId('bucket-name-form-group');
- const findBucketName = () => wrapper.findByTestId('bucket-name');
- const findSecretAccessKeyFormGroup = () => wrapper.findByTestId('secret-access-key-form-group');
- const findSecretAccessKey = () => wrapper.findByTestId('secret-access-key');
- const findSecretAccessKeyAddButton = () => wrapper.findByTestId('secret-access-key-add-button');
- const findSecretAccessKeyCancelButton = () =>
- wrapper.findByTestId('secret-access-key-cancel-button');
-
- afterEach(() => {
- createAlert.mockClear();
- });
-
- describe('when useConsolidatedAuditEventStreamDestApi is enabled', () => {
- const item = mockAwsTypeDestination[0];
-
- beforeEach(() => {
- createComponent({
- props: { item },
- provide: {
- glFeatures: { useConsolidatedAuditEventStreamDestApi: true },
- },
- });
- });
-
- it('renders the destination correctly', () => {
- expect(findName().element.value).toBe('AWS Destination 1');
- expect(findAccessKeyXid().element.value).toBe('myAwsAccessKey_needs_16_chars_min');
- expect(findAwsRegion().element.value).toBe('us-test-1');
- expect(findBucketName().element.value).toBe('bucket-name');
- expect(findSecretAccessKey().exists()).toBe(false);
- expect(findSecretAccessKeyAddButton().exists()).toBe(true);
- expect(findSecretAccessKeyCancelButton().exists()).toBe(false);
- });
- });
-
- describe('Group amazon S3 stream destination editor', () => {
- describe('when initialized', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('should render the destinations warning', () => {
- expect(findWarningMessage().props('title')).toBe(ADD_STREAM_EDITOR_I18N.WARNING_TITLE);
- });
-
- it('should render the destination name input', () => {
- expect(findNameFormGroup().exists()).toBe(true);
- expect(findName().exists()).toBe(true);
- expect(findName().attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.AMAZON_S3_DESTINATION_NAME_PLACEHOLDER,
- );
- });
-
- it('should render the destination AccessKeyXid input', () => {
- expect(findAccessKeyXidFormGroup().exists()).toBe(true);
- expect(findAccessKeyXid().exists()).toBe(true);
- expect(findAccessKeyXid().attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.AMAZON_S3_DESTINATION_ACCESS_KEY_XID_PLACEHOLDER,
- );
- });
-
- it('should render the destination awsRegion input', () => {
- expect(findAwsRegionFormGroup().exists()).toBe(true);
- expect(findAwsRegion().exists()).toBe(true);
- expect(findAwsRegion().attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.AMAZON_S3_DESTINATION_AWS_REGION_PLACEHOLDER,
- );
- });
-
- it('should render the destination BucketName input', () => {
- expect(findBucketNameFormGroup().exists()).toBe(true);
- expect(findBucketName().exists()).toBe(true);
- expect(findBucketName().attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.AMAZON_S3_DESTINATION_BUCKET_NAME_PLACEHOLDER,
- );
- });
-
- it('should not render the destination Secret Access Key input', () => {
- expect(findSecretAccessKeyFormGroup().exists()).toBe(true);
- expect(findSecretAccessKey().exists()).toBe(true);
- });
-
- it('does not render the delete button', () => {
- expect(findDeleteBtn().exists()).toBe(false);
- });
-
- it('renders the add button text', () => {
- expect(findSubmitStreamBtn().attributes('name')).toBe(
- ADD_STREAM_EDITOR_I18N.ADD_BUTTON_NAME,
- );
- expect(findSubmitStreamBtn().text()).toBe(ADD_STREAM_EDITOR_I18N.ADD_BUTTON_TEXT);
- });
-
- it('disables the add button at first', () => {
- expect(findSubmitStreamBtn().props('disabled')).toBe(true);
- });
- });
-
- describe('add destination event', () => {
- it('should emit add event after destination added', async () => {
- createComponent();
-
- await findName().setValue(mockAmazonS3Destinations[0].name);
- await findAccessKeyXid().setValue(mockAmazonS3Destinations[0].accessKeyXid);
- await findAwsRegion().setValue(mockAmazonS3Destinations[0].awsRegion);
- await findBucketName().setValue(mockAmazonS3Destinations[0].bucketName);
- await findSecretAccessKey().setValue(mockAmazonS3Destinations[0].secretAccessKey);
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(false);
-
- await findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('added')).toBeDefined();
- });
-
- it('should not emit add destination event and reports error when server returns error', async () => {
- const errorMsg = 'Destination hosts limit exceeded';
- createComponent({
- apolloHandlers: [
- [
- amazonS3ConfigurationCreate,
- jest.fn().mockResolvedValue(amazonS3DestinationCreateMutationPopulator([errorMsg])),
- ],
- ],
- });
-
- findName().setValue(mockAmazonS3Destinations[0].name);
- findAccessKeyXid().setValue(mockAmazonS3Destinations[0].accessKeyXid);
- findAwsRegion().setValue(mockAmazonS3Destinations[0].awsRegion);
- findBucketName().setValue(mockAmazonS3Destinations[0].bucketName);
- findSecretAccessKey().setValue(mockAmazonS3Destinations[0].secretAccessKey);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(errorMsg);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('added')).toBeUndefined();
- });
-
- it('should not emit add destination event and reports error when network error occurs', async () => {
- const sentryError = new Error('Network error');
- const sentryCaptureExceptionSpy = jest.spyOn(Sentry, 'captureException');
- createComponent({
- apolloHandlers: [[amazonS3ConfigurationCreate, jest.fn().mockRejectedValue(sentryError)]],
- });
-
- findName().setValue(mockAmazonS3Destinations[0].name);
- findAccessKeyXid().setValue(mockAmazonS3Destinations[0].accessKeyXid);
- findAwsRegion().setValue(mockAmazonS3Destinations[0].awsRegion);
- findBucketName().setValue(mockAmazonS3Destinations[0].bucketName);
- findSecretAccessKey().setValue(mockAmazonS3Destinations[0].secretAccessKey);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(AUDIT_STREAMS_NETWORK_ERRORS.CREATING_ERROR);
- expect(sentryCaptureExceptionSpy).toHaveBeenCalledWith(sentryError);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('added')).toBeUndefined();
- });
- });
-
- describe('cancel event', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('should emit cancel event correctly', () => {
- findCancelStreamBtn().vm.$emit('click');
-
- expect(wrapper.emitted('cancel')).toBeDefined();
- });
- });
-
- describe('when editing an existing destination', () => {
- describe('renders', () => {
- beforeEach(() => {
- createComponent({ props: { item: mockAmazonS3Destinations[0] } });
- });
-
- it('the destination fields', () => {
- expect(findName().element.value).toBe(mockAmazonS3Destinations[0].name);
- expect(findAccessKeyXid().element.value).toBe(mockAmazonS3Destinations[0].accessKeyXid);
- expect(findAwsRegion().element.value).toBe(mockAmazonS3Destinations[0].awsRegion);
- expect(findBucketName().element.value).toBe(mockAmazonS3Destinations[0].bucketName);
- expect(findSecretAccessKey().exists()).toBe(false);
- expect(findSecretAccessKeyAddButton().exists()).toBe(true);
- expect(findSecretAccessKeyCancelButton().exists()).toBe(false);
- });
-
- it('the delete button', () => {
- expect(findDeleteBtn().exists()).toBe(true);
- });
-
- it('renders the save button text', () => {
- expect(findSubmitStreamBtn().attributes('name')).toBe(
- ADD_STREAM_EDITOR_I18N.SAVE_BUTTON_NAME,
- );
- expect(findSubmitStreamBtn().text()).toBe(ADD_STREAM_EDITOR_I18N.SAVE_BUTTON_TEXT);
- });
-
- it('disables the save button at first', () => {
- expect(findSubmitStreamBtn().props('disabled')).toBe(true);
- });
-
- it('displays the secret access key field when adding', async () => {
- await findSecretAccessKeyAddButton().trigger('click');
-
- expect(findSecretAccessKeyAddButton().props('disabled')).toBe(true);
- expect(findSecretAccessKeyCancelButton().exists()).toBe(true);
- expect(findSecretAccessKey().element.value).toBe('');
- });
-
- it('removes the secret access key field when cancelled', async () => {
- await findSecretAccessKeyAddButton().trigger('click');
- await findSecretAccessKeyCancelButton().trigger('click');
-
- expect(findSecretAccessKeyAddButton().props('disabled')).toBe(false);
- expect(findSecretAccessKey().exists()).toBe(false);
- expect(findSecretAccessKeyAddButton().exists()).toBe(true);
- expect(findSecretAccessKeyCancelButton().exists()).toBe(false);
- });
- });
-
- it.each`
- name | findInputFn
- ${'Destination Name'} | ${findName}
- ${'Access Key Xid'} | ${findAccessKeyXid}
- ${'AWS Region'} | ${findAwsRegion}
- ${'Bucket Name'} | ${findBucketName}
- `('enable the save button when $name is edited', async ({ findInputFn }) => {
- createComponent({ props: { item: mockAmazonS3Destinations[0] } });
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(true);
-
- await findInputFn().setValue('test');
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(false);
- });
-
- it('should emit updated event after destination updated', async () => {
- createComponent({
- props: { item: mockAmazonS3Destinations[0] },
- apolloHandlers: [
- [
- amazonS3ConfigurationUpdate,
- jest.fn().mockResolvedValue(amazonS3DestinationUpdateMutationPopulator()),
- ],
- ],
- });
-
- findName().setValue(mockAmazonS3Destinations[0].name);
- findAccessKeyXid().setValue(mockAmazonS3Destinations[1].accessKeyXid);
- findAwsRegion().setValue(mockAmazonS3Destinations[1].awsRegion);
- findBucketName().setValue(mockAmazonS3Destinations[1].bucketName);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('updated')).toBeDefined();
- });
-
- it('should emit updated event after destination secret access key updated', async () => {
- createComponent({
- props: { item: mockAmazonS3Destinations[0] },
- apolloHandlers: [
- [
- amazonS3ConfigurationUpdate,
- jest.fn().mockResolvedValue(amazonS3DestinationUpdateMutationPopulator()),
- ],
- ],
- });
-
- await findSecretAccessKeyAddButton().trigger('click');
-
- findSecretAccessKey().setValue(mockAmazonS3Destinations[1].secretAccessKey);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('updated')).toBeDefined();
- });
-
- it('should not emit add destination event and reports error when server returns error', async () => {
- const errorMsg = 'Destination hosts limit exceeded';
- createComponent({
- props: { item: mockAmazonS3Destinations[0] },
- apolloHandlers: [
- [
- amazonS3ConfigurationUpdate,
- jest.fn().mockResolvedValue(amazonS3DestinationUpdateMutationPopulator([errorMsg])),
- ],
- ],
- });
-
- findName().setValue(mockAmazonS3Destinations[0].name);
- findAccessKeyXid().setValue(mockAmazonS3Destinations[0].accessKeyXid);
- findAwsRegion().setValue(mockAmazonS3Destinations[0].awsRegion);
- findBucketName().setValue(mockAmazonS3Destinations[0].bucketName);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(errorMsg);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('updated')).toBeUndefined();
- });
-
- it('should not emit add destination event and reports error when network error occurs', async () => {
- const sentryError = new Error('Network error');
- const sentryCaptureExceptionSpy = jest.spyOn(Sentry, 'captureException');
- createComponent({
- props: { item: mockAmazonS3Destinations[0] },
- apolloHandlers: [[amazonS3ConfigurationUpdate, jest.fn().mockRejectedValue(sentryError)]],
- });
-
- findName().setValue(mockAmazonS3Destinations[0].name);
- findAccessKeyXid().setValue(mockAmazonS3Destinations[0].accessKeyXid);
- findAwsRegion().setValue(mockAmazonS3Destinations[0].awsRegion);
- findBucketName().setValue(mockAmazonS3Destinations[0].bucketName);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(AUDIT_STREAMS_NETWORK_ERRORS.UPDATING_ERROR);
- expect(sentryCaptureExceptionSpy).toHaveBeenCalledWith(sentryError);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('updated')).toBeUndefined();
- });
- });
-
- describe('deleting', () => {
- beforeEach(() => {
- createComponent({ props: { item: mockAmazonS3Destinations[0] } });
- });
-
- it('should emit deleted on success operation', async () => {
- const deleteButton = findDeleteBtn();
- await deleteButton.trigger('click');
- await findDeleteModal().vm.$emit('deleting');
-
- expect(deleteButton.props('loading')).toBe(true);
-
- await findDeleteModal().vm.$emit('delete');
-
- expect(deleteButton.props('loading')).toBe(false);
- expect(wrapper.emitted('deleted')).toEqual([[mockAmazonS3Destinations[0].id]]);
- });
-
- it('shows the alert for the error', () => {
- const errorMsg = 'An error occurred';
- findDeleteModal().vm.$emit('error', errorMsg);
-
- expect(createAlert).toHaveBeenCalledWith({
- message: AUDIT_STREAMS_NETWORK_ERRORS.DELETING_ERROR,
- captureError: true,
- error: errorMsg,
- });
- });
- });
-
- it('passes actual newlines when these are used in the secret access key input', async () => {
- const mutationMock = jest
- .fn()
- .mockResolvedValue(amazonS3DestinationCreateMutationPopulator());
- createComponent({
- apolloHandlers: [[amazonS3ConfigurationCreate, mutationMock]],
- });
-
- await findSecretAccessKey().setValue('\\ntest\\n');
- await findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
-
- expect(mutationMock).toHaveBeenCalledWith(
- expect.objectContaining({
- secretAccessKey: '\ntest\n',
- }),
- );
- });
- });
-
- describe('Instance amazon S3 stream destination editor', () => {
- beforeEach(() => {
- groupPathProvide = instanceGroupPath;
- });
-
- describe('when initialized', () => {
- beforeEach(() => {
- createComponent({
- apolloHandlers: [
- [
- instanceAmazonS3ConfigurationCreate,
- jest.fn().mockResolvedValue(instanceAmazonS3DestinationCreateMutationPopulator()),
- ],
- ],
- });
- });
-
- it('should render the destinations warning', () => {
- expect(findWarningMessage().props('title')).toBe(ADD_STREAM_EDITOR_I18N.WARNING_TITLE);
- });
-
- it('should render the destination name input', () => {
- expect(findNameFormGroup().exists()).toBe(true);
- expect(findName().exists()).toBe(true);
- expect(findName().attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.AMAZON_S3_DESTINATION_NAME_PLACEHOLDER,
- );
- });
-
- it('should render the destination AccessKeyXid input', () => {
- expect(findAccessKeyXidFormGroup().exists()).toBe(true);
- expect(findAccessKeyXid().exists()).toBe(true);
- expect(findAccessKeyXid().attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.AMAZON_S3_DESTINATION_ACCESS_KEY_XID_PLACEHOLDER,
- );
- });
-
- it('should render the destination awsRegion input', () => {
- expect(findAwsRegionFormGroup().exists()).toBe(true);
- expect(findAwsRegion().exists()).toBe(true);
- expect(findAwsRegion().attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.AMAZON_S3_DESTINATION_AWS_REGION_PLACEHOLDER,
- );
- });
-
- it('should render the destination BucketName input', () => {
- expect(findBucketNameFormGroup().exists()).toBe(true);
- expect(findBucketName().exists()).toBe(true);
- expect(findBucketName().attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.AMAZON_S3_DESTINATION_BUCKET_NAME_PLACEHOLDER,
- );
- });
-
- it('should render the destination Secret Access Key input', () => {
- expect(findSecretAccessKeyFormGroup().exists()).toBe(true);
- expect(findSecretAccessKey().exists()).toBe(true);
- });
-
- it('does not render the delete button', () => {
- expect(findDeleteBtn().exists()).toBe(false);
- });
-
- it('renders the add button text', () => {
- expect(findSubmitStreamBtn().attributes('name')).toBe(
- ADD_STREAM_EDITOR_I18N.ADD_BUTTON_NAME,
- );
- expect(findSubmitStreamBtn().text()).toBe(ADD_STREAM_EDITOR_I18N.ADD_BUTTON_TEXT);
- });
-
- it('disables the add button at first', () => {
- expect(findSubmitStreamBtn().props('disabled')).toBe(true);
- });
- });
-
- describe('when add destination event', () => {
- describe('successfully added', () => {
- beforeEach(() => {
- createComponent({
- apolloHandlers: [
- [
- instanceAmazonS3ConfigurationCreate,
- jest.fn().mockResolvedValue(instanceAmazonS3DestinationCreateMutationPopulator()),
- ],
- ],
- });
-
- findName().setValue(mockInstanceAmazonS3Destinations[0].name);
- findAccessKeyXid().setValue(mockInstanceAmazonS3Destinations[0].accessKeyXid);
- findAwsRegion().setValue(mockInstanceAmazonS3Destinations[0].awsRegion);
- findBucketName().setValue(mockInstanceAmazonS3Destinations[0].bucketName);
- findSecretAccessKey().setValue(mockInstanceAmazonS3Destinations[0].secretAccessKey);
- });
- it('add stream button should be disabled to start', () => {
- expect(findSubmitStreamBtn().props('disabled')).toBe(false);
- });
-
- it('should emit add event after destination added', async () => {
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(wrapper.emitted('added')).toBeDefined();
- });
- it('should not emit error event after destination added', async () => {
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- });
- });
-
- describe('when server returns error', () => {
- const errorMsg = 'Destination hosts limit exceeded';
- beforeEach(() => {
- createComponent({
- apolloHandlers: [
- [
- instanceAmazonS3ConfigurationCreate,
- jest
- .fn()
- .mockResolvedValue(
- instanceAmazonS3DestinationCreateMutationPopulator([errorMsg]),
- ),
- ],
- ],
- });
- findName().setValue(mockInstanceAmazonS3Destinations[0].name);
- findAccessKeyXid().setValue(mockInstanceAmazonS3Destinations[0].accessKeyXid);
- findAwsRegion().setValue(mockInstanceAmazonS3Destinations[0].awsRegion);
- findBucketName().setValue(mockInstanceAmazonS3Destinations[0].bucketName);
- findSecretAccessKey().setValue(mockInstanceAmazonS3Destinations[0].secretAccessKey);
- });
-
- it('add stream button should be disabled to start', () => {
- expect(findSubmitStreamBtn().props('disabled')).toBe(false);
- });
-
- it('should not emit add event after destination added', async () => {
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(wrapper.emitted('added')).toBeUndefined();
- });
- it('should emit error event after destination added', async () => {
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(errorMsg);
- expect(wrapper.emitted('error')).toBeDefined();
- });
- });
-
- describe('when network errors', () => {
- const sentryError = new Error('Network error');
- let sentryCaptureExceptionSpy;
-
- beforeEach(async () => {
- sentryCaptureExceptionSpy = jest.spyOn(Sentry, 'captureException');
- createComponent({
- apolloHandlers: [
- [instanceAmazonS3ConfigurationCreate, jest.fn().mockRejectedValue(sentryError)],
- ],
- });
-
- findName().setValue(mockInstanceAmazonS3Destinations[0].name);
- findAccessKeyXid().setValue(mockInstanceAmazonS3Destinations[0].accessKeyXid);
- findAwsRegion().setValue(mockInstanceAmazonS3Destinations[0].awsRegion);
- findBucketName().setValue(mockInstanceAmazonS3Destinations[0].bucketName);
- findSecretAccessKey().setValue(mockInstanceAmazonS3Destinations[0].secretAccessKey);
-
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
- });
-
- it('shows error alerts', () => {
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(AUDIT_STREAMS_NETWORK_ERRORS.CREATING_ERROR);
- });
-
- it('logs to Sentry', () => {
- expect(sentryCaptureExceptionSpy).toHaveBeenCalledWith(sentryError);
- });
-
- it('emits correct events', () => {
- expect(wrapper.emitted('error')).toBeDefined();
-
- expect(wrapper.emitted('added')).toBeUndefined();
- });
- });
- });
-
- describe('cancel event', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('should emit cancel event correctly', () => {
- findCancelStreamBtn().vm.$emit('click');
-
- expect(wrapper.emitted('cancel')).toBeDefined();
- });
- });
-
- describe('when editing an existing destination', () => {
- describe('renders', () => {
- beforeEach(() => {
- createComponent({ props: { item: mockInstanceAmazonS3Destinations[0] } });
- });
-
- it('the name field', () => {
- expect(findName().exists()).toBe(true);
- expect(findName().element.value).toBe(mockInstanceAmazonS3Destinations[0].name);
- });
- it('the access Key id field', () => {
- expect(findAccessKeyXid().exists()).toBe(true);
- expect(findAccessKeyXid().element.value).toBe(
- mockInstanceAmazonS3Destinations[0].accessKeyXid,
- );
- });
- it('the aws Region field', () => {
- expect(findAwsRegion().exists()).toBe(true);
- expect(findAwsRegion().element.value).toBe(mockInstanceAmazonS3Destinations[0].awsRegion);
- });
- it('the bucket Name field', () => {
- expect(findBucketName().exists()).toBe(true);
- expect(findBucketName().element.value).toBe(
- mockInstanceAmazonS3Destinations[0].bucketName,
- );
- });
- it('the Secret Access Key field', () => {
- expect(findSecretAccessKey().exists()).toBe(false);
- expect(findSecretAccessKeyAddButton().exists()).toBe(true);
- expect(findSecretAccessKeyCancelButton().exists()).toBe(false);
- });
-
- it('the delete button', () => {
- expect(findDeleteBtn().exists()).toBe(true);
- });
-
- it('renders the save button text', () => {
- expect(findSubmitStreamBtn().attributes('name')).toBe(
- ADD_STREAM_EDITOR_I18N.SAVE_BUTTON_NAME,
- );
- expect(findSubmitStreamBtn().text()).toBe(ADD_STREAM_EDITOR_I18N.SAVE_BUTTON_TEXT);
- });
-
- it('disables the save button at first', () => {
- expect(findSubmitStreamBtn().props('disabled')).toBe(true);
- });
-
- it('displays the secret access key field when adding', async () => {
- await findSecretAccessKeyAddButton().trigger('click');
-
- expect(findSecretAccessKeyAddButton().props('disabled')).toBe(true);
- expect(findSecretAccessKeyCancelButton().exists()).toBe(true);
- expect(findSecretAccessKey().element.value).toBe('');
- });
-
- it('removes the secret access key field when cancelled', async () => {
- await findSecretAccessKeyAddButton().trigger('click');
- await findSecretAccessKeyCancelButton().trigger('click');
-
- expect(findSecretAccessKeyAddButton().props('disabled')).toBe(false);
- expect(findSecretAccessKey().exists()).toBe(false);
- expect(findSecretAccessKeyAddButton().exists()).toBe(true);
- expect(findSecretAccessKeyCancelButton().exists()).toBe(false);
- });
- });
-
- describe.each`
- name | findInputFn
- ${'Destination Name'} | ${findName}
- ${'Access Key Xid'} | ${findAccessKeyXid}
- ${'AWS Region'} | ${findAwsRegion}
- ${'Bucket Name'} | ${findBucketName}
- `('enable the save button when $name is edited', ({ findInputFn }) => {
- beforeEach(() => {
- createComponent({ props: { item: mockInstanceAmazonS3Destinations[0] } });
- });
-
- it('should have save button disabled', () => {
- expect(findSubmitStreamBtn().props('disabled')).toBe(true);
- });
-
- it('should have save button enabled', async () => {
- await findInputFn().setValue('test');
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(false);
- });
- });
-
- describe('when destination updated', () => {
- beforeEach(async () => {
- createComponent({
- props: { item: mockInstanceAmazonS3Destinations[0] },
- apolloHandlers: [
- [
- instanceAmazonS3ConfigurationUpdate,
- jest.fn().mockResolvedValue(instanceAmazonS3DestinationUpdateMutationPopulator()),
- ],
- ],
- });
-
- findName().setValue(mockInstanceAmazonS3Destinations[0].name);
- findAccessKeyXid().setValue(mockInstanceAmazonS3Destinations[1].accessKeyXid);
- findAwsRegion().setValue(mockInstanceAmazonS3Destinations[1].awsRegion);
- findBucketName().setValue(mockInstanceAmazonS3Destinations[1].bucketName);
-
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
- });
-
- it('should emit updated event', () => {
- expect(wrapper.emitted('updated')).toBeDefined();
- });
- it('not emit error event', () => {
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- });
- });
-
- describe('when destination secret access key updated', () => {
- beforeEach(async () => {
- createComponent({
- props: { item: mockInstanceAmazonS3Destinations[0] },
- apolloHandlers: [
- [
- instanceAmazonS3ConfigurationUpdate,
- jest.fn().mockResolvedValue(instanceAmazonS3DestinationUpdateMutationPopulator()),
- ],
- ],
- });
-
- await findSecretAccessKeyAddButton().trigger('click');
- findSecretAccessKey().setValue(mockInstanceAmazonS3Destinations[1].secretAccessKey);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
- });
-
- it('should emit updated event', () => {
- expect(wrapper.emitted('updated')).toBeDefined();
- });
- it('should not emit error event', () => {
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- });
- });
-
- describe('when server returns error', () => {
- const errorMsg = 'Destination hosts limit exceeded';
-
- beforeEach(async () => {
- createComponent({
- props: { item: mockInstanceAmazonS3Destinations[0] },
- apolloHandlers: [
- [
- instanceAmazonS3ConfigurationUpdate,
- jest
- .fn()
- .mockResolvedValue(
- instanceAmazonS3DestinationUpdateMutationPopulator([errorMsg]),
- ),
- ],
- ],
- });
-
- findName().setValue(mockInstanceAmazonS3Destinations[0].name);
- findAccessKeyXid().setValue(mockInstanceAmazonS3Destinations[0].accessKeyXid);
- findAwsRegion().setValue(mockInstanceAmazonS3Destinations[0].awsRegion);
- findBucketName().setValue(mockInstanceAmazonS3Destinations[0].bucketName);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
- });
-
- it('should report error', () => {
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(errorMsg);
- expect(wrapper.emitted('error')).toBeDefined();
- });
- it('should not emit updated destination event', () => {
- expect(wrapper.emitted('updated')).toBeUndefined();
- });
- });
-
- describe('when network errors', () => {
- const sentryError = new Error('Network error');
- let sentryCaptureExceptionSpy;
-
- beforeEach(async () => {
- sentryCaptureExceptionSpy = jest.spyOn(Sentry, 'captureException');
- createComponent({
- props: { item: mockInstanceAmazonS3Destinations[0] },
- apolloHandlers: [
- [instanceAmazonS3ConfigurationUpdate, jest.fn().mockRejectedValue(sentryError)],
- ],
- });
-
- findName().setValue(mockInstanceAmazonS3Destinations[0].name);
- findAccessKeyXid().setValue(mockInstanceAmazonS3Destinations[0].accessKeyXid);
- findAwsRegion().setValue(mockInstanceAmazonS3Destinations[0].awsRegion);
- findBucketName().setValue(mockInstanceAmazonS3Destinations[0].bucketName);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
- });
-
- it('shows error alerts', () => {
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(AUDIT_STREAMS_NETWORK_ERRORS.UPDATING_ERROR);
- });
-
- it('logs to Sentry', () => {
- expect(sentryCaptureExceptionSpy).toHaveBeenCalledWith(sentryError);
- });
-
- it('emits correct events', () => {
- expect(wrapper.emitted('error')).toBeDefined();
-
- expect(wrapper.emitted('added')).toBeUndefined();
- });
- });
- });
-
- describe('deleting', () => {
- beforeEach(async () => {
- createComponent({ props: { item: mockInstanceAmazonS3Destinations[0] } });
- await findDeleteBtn().trigger('click');
- });
-
- it('should emit deleting on success operation', async () => {
- await findDeleteModal().vm.$emit('deleting');
-
- expect(findDeleteBtn().props('loading')).toBe(true);
- });
-
- it('should emit deleted on success operation', async () => {
- await findDeleteModal().vm.$emit('delete');
-
- expect(findDeleteBtn().props('loading')).toBe(false);
- expect(wrapper.emitted('deleted')).toEqual([[mockInstanceAmazonS3Destinations[0].id]]);
- });
-
- it('shows the alert for the error', () => {
- const errorMsg = 'An error occurred';
- findDeleteModal().vm.$emit('error', errorMsg);
-
- expect(createAlert).toHaveBeenCalledWith({
- message: AUDIT_STREAMS_NETWORK_ERRORS.DELETING_ERROR,
- captureError: true,
- error: errorMsg,
- });
- });
- });
-
- it('passes actual newlines when these are used in the secret access key input', async () => {
- const mutationMock = jest
- .fn()
- .mockResolvedValue(instanceAmazonS3DestinationCreateMutationPopulator());
- createComponent({
- apolloHandlers: [[instanceAmazonS3ConfigurationCreate, mutationMock]],
- });
-
- await findSecretAccessKey().setValue('\\ntest\\n');
- await findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
-
- expect(mutationMock).toHaveBeenCalledWith(
- expect.objectContaining({
- secretAccessKey: '\ntest\n',
- }),
- );
- });
- });
-});
diff --git a/ee/spec/frontend/audit_events/components/stream/stream_delete_modal_spec.js b/ee/spec/frontend/audit_events/components/stream/stream_delete_modal_spec.js
index cd4ab6f5a1fc7fdcd9e67bb8b989d4770bc47f15..914452ba8071ffeb6c01d550e3fbb179b93935fc 100644
--- a/ee/spec/frontend/audit_events/components/stream/stream_delete_modal_spec.js
+++ b/ee/spec/frontend/audit_events/components/stream/stream_delete_modal_spec.js
@@ -5,33 +5,10 @@ import VueApollo from 'vue-apollo';
import StreamDeleteModal from 'ee/audit_events/components/stream/stream_delete_modal.vue';
import deleteGroupStreamingDestinationsQuery from 'ee/audit_events/graphql/mutations/delete_group_streaming_destination.mutation.graphql';
-import deleteExternalDestination from 'ee/audit_events/graphql/mutations/delete_external_destination.mutation.graphql';
-import deleteInstanceExternalDestination from 'ee/audit_events/graphql/mutations/delete_instance_external_destination.mutation.graphql';
-import googleCloudLoggingConfigurationDestroy from 'ee/audit_events/graphql/mutations/delete_gcp_logging_destination.mutation.graphql';
-import instanceGoogleCloudLoggingConfigurationDestroy from 'ee/audit_events/graphql/mutations/delete_instance_gcp_logging_destination.mutation.graphql';
-import amazonS3ConfigurationDestroy from 'ee/audit_events/graphql/mutations/delete_amazon_s3_destination.mutation.graphql';
-import instanceAmazonS3ConfigurationDestroy from 'ee/audit_events/graphql/mutations/delete_instance_amazon_s3_destination.mutation.graphql';
+import deleteInstanceStreamingDestinationsQuery from 'ee/audit_events/graphql/mutations/delete_instance_streaming_destination.mutation.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import {
- groupPath,
- destinationDeleteMutationPopulator,
- mockExternalDestinations,
- mockHttpType,
- mockGcpLoggingType,
- instanceGroupPath,
- destinationInstanceDeleteMutationPopulator,
- mockInstanceExternalDestinations,
- mockGcpLoggingDestinations,
- destinationGcpLoggingDeleteMutationPopulator,
- mockInstanceGcpLoggingDestinations,
- destinationInstanceGcpLoggingDeleteMutationPopulator,
- mockAmazonS3Destinations,
- mockAmazonS3Type,
- destinationAmazonS3DeleteMutationPopulator,
- mockInstanceAmazonS3Destinations,
- destinationInstanceAmazonS3DeleteMutationPopulator,
-} from '../../mock_data';
+import { groupPath, instanceGroupPath } from '../../mock_data';
import {
mockHttpTypeDestination,
streamingDestinationDeleteMutationPopulator,
@@ -42,12 +19,6 @@ Vue.use(VueApollo);
describe('StreamDeleteModal', () => {
let wrapper;
- const instanceDestination = mockInstanceExternalDestinations[0];
- const gcpLoggingDestination = mockGcpLoggingDestinations[0];
- const instanceGcpLoggingDestination = mockInstanceGcpLoggingDestinations[0];
- const mockAmazonS3Destination = mockAmazonS3Destinations[0];
- const instanceAmazonS3Destination = mockInstanceAmazonS3Destinations[0];
-
const deleteStreamingSuccess = jest
.fn()
.mockResolvedValue(streamingDestinationDeleteMutationPopulator());
@@ -58,38 +29,37 @@ describe('StreamDeleteModal', () => {
.fn()
.mockRejectedValue(streamingDestinationDeleteMutationPopulator(['Network error']));
- const deleteSuccess = jest.fn().mockResolvedValue(destinationDeleteMutationPopulator());
- const deleteInstanceSuccess = jest
- .fn()
- .mockResolvedValue(destinationInstanceDeleteMutationPopulator());
- const deleteGcpLoggingSuccess = jest
- .fn()
- .mockResolvedValue(destinationGcpLoggingDeleteMutationPopulator());
- const deleteInstanceGcpLoggingSuccess = jest
- .fn()
- .mockResolvedValue(destinationInstanceGcpLoggingDeleteMutationPopulator());
- const deleteAmazonS3Success = jest
- .fn()
- .mockResolvedValue(destinationAmazonS3DeleteMutationPopulator());
- const deleteInstanceAmazonS3Success = jest
- .fn()
- .mockResolvedValue(destinationInstanceAmazonS3DeleteMutationPopulator());
- const deleteError = jest
- .fn()
- .mockResolvedValue(destinationDeleteMutationPopulator(['Random Error message']));
- const deleteNetworkError = jest
- .fn()
- .mockRejectedValue(destinationDeleteMutationPopulator(['Network error']));
+ const deleteInstanceStreamingSuccess = jest.fn().mockResolvedValue({
+ data: {
+ instanceAuditEventStreamingDestinationsDelete: {
+ errors: [],
+ },
+ },
+ });
+ const deleteInstanceStreamingError = jest.fn().mockResolvedValue({
+ data: {
+ instanceAuditEventStreamingDestinationsDelete: {
+ errors: ['Random Error message'],
+ },
+ },
+ });
+ const deleteInstanceStreamingNetworkError = jest.fn().mockRejectedValue({
+ data: {
+ instanceAuditEventStreamingDestinationsDelete: {
+ errors: ['Network error'],
+ },
+ },
+ });
let groupPathProvide = groupPath;
- let itemProvide = mockExternalDestinations[0];
- let typeProvide = mockHttpType;
- let deleteExternalDestinationProvide = deleteExternalDestination;
+ let itemProvide = mockHttpTypeDestination[0];
+ const typeProvide = 'http'; // Type is required by component but not used in consolidated API
+ let deleteExternalDestinationProvide = deleteGroupStreamingDestinationsQuery;
const findModal = () => wrapper.findComponent(GlModal);
const clickDeleteFramework = () => findModal().vm.$emit('primary');
- const createComponent = (resolverMock, useConsolidatedAuditEventStreamDestApi = false) => {
+ const createComponent = (resolverMock) => {
const mockApollo = createMockApollo([[deleteExternalDestinationProvide, resolverMock]]);
wrapper = shallowMount(StreamDeleteModal, {
@@ -100,9 +70,6 @@ describe('StreamDeleteModal', () => {
},
provide: {
groupPath: groupPathProvide,
- glFeatures: {
- useConsolidatedAuditEventStreamDestApi,
- },
},
stubs: {
GlSprintf,
@@ -132,180 +99,32 @@ describe('StreamDeleteModal', () => {
});
describe('Group HTTP clickDeleteDestination', () => {
- it('emits "deleting" event when busy deleting', () => {
- createComponent();
- clickDeleteFramework();
-
- expect(wrapper.emitted('deleting')).toHaveLength(1);
- });
-
- it('calls the delete mutation with the destination ID', async () => {
- createComponent(deleteSuccess);
- clickDeleteFramework();
-
- await waitForPromises();
-
- expect(deleteSuccess).toHaveBeenCalledWith({
- id: mockExternalDestinations[0].id,
- isInstance: false,
- });
- });
-
- it('emits "delete" event when the destination is successfully deleted', async () => {
- createComponent(deleteSuccess);
- clickDeleteFramework();
-
- await waitForPromises();
-
- expect(wrapper.emitted('delete')).toHaveLength(1);
- });
-
- it('emits "error" event when there is a network error', async () => {
- createComponent(deleteNetworkError);
- clickDeleteFramework();
-
- await waitForPromises();
-
- expect(wrapper.emitted('error')).toHaveLength(1);
- });
-
- it('emits "error" event when there is a graphql error', async () => {
- createComponent(deleteError);
- clickDeleteFramework();
-
- await waitForPromises();
-
- expect(wrapper.emitted('error')).toHaveLength(1);
- });
- describe('with useConsolidatedAuditEventStreamDestApi feature flag', () => {
- beforeEach(() => {
- deleteExternalDestinationProvide = deleteGroupStreamingDestinationsQuery;
- [itemProvide] = mockHttpTypeDestination;
- });
-
- it('calls the delete mutation with the destination ID', async () => {
- createComponent(deleteStreamingSuccess, true);
- clickDeleteFramework();
-
- await waitForPromises();
-
- expect(deleteStreamingSuccess).toHaveBeenCalledWith({
- id: mockHttpTypeDestination[0].id,
- isInstance: false,
- });
- });
-
- it('emits "delete" event when the destination is successfully deleted', async () => {
- createComponent(deleteStreamingSuccess, true);
- clickDeleteFramework();
-
- await waitForPromises();
-
- expect(wrapper.emitted('delete')).toHaveLength(1);
- });
-
- it('emits "error" event when there is a network error', async () => {
- createComponent(deleteStreamingNetworkError, true);
- clickDeleteFramework();
-
- await waitForPromises();
-
- expect(wrapper.emitted('error')).toHaveLength(1);
- });
-
- it('emits "error" event when there is a graphql error', async () => {
- createComponent(deleteStreamingError, true);
- clickDeleteFramework();
-
- await waitForPromises();
-
- expect(wrapper.emitted('error')).toHaveLength(1);
- });
- });
- });
-
- describe('Group GCP Logging clickDeleteDestination', () => {
- beforeEach(() => {
- itemProvide = gcpLoggingDestination;
- typeProvide = mockGcpLoggingType;
- deleteExternalDestinationProvide = googleCloudLoggingConfigurationDestroy;
- });
-
- it('emits "deleting" event when busy deleting', () => {
- createComponent();
- clickDeleteFramework();
-
- expect(wrapper.emitted('deleting')).toHaveLength(1);
- });
-
- it('calls the delete mutation with the destination ID', async () => {
- createComponent(deleteGcpLoggingSuccess);
- clickDeleteFramework();
-
- await waitForPromises();
-
- expect(deleteGcpLoggingSuccess).toHaveBeenCalledWith({
- id: mockGcpLoggingDestinations[0].id,
- isInstance: false,
- });
- });
-
- it('emits "delete" event when the destination is successfully deleted', async () => {
- createComponent(deleteGcpLoggingSuccess);
- clickDeleteFramework();
-
- await waitForPromises();
-
- expect(wrapper.emitted('delete')).toHaveLength(1);
- });
-
- it('emits "error" event when there is a network error', async () => {
- createComponent(deleteNetworkError);
- clickDeleteFramework();
-
- await waitForPromises();
-
- expect(wrapper.emitted('error')).toHaveLength(1);
- });
-
- it('emits "error" event when there is a graphql error', async () => {
- createComponent(deleteError);
- clickDeleteFramework();
-
- await waitForPromises();
-
- expect(wrapper.emitted('error')).toHaveLength(1);
- });
- });
-
- describe('Group Amazon S3 clickDeleteDestination', () => {
beforeEach(() => {
- itemProvide = mockAmazonS3Destination;
- typeProvide = mockAmazonS3Type;
- deleteExternalDestinationProvide = amazonS3ConfigurationDestroy;
+ deleteExternalDestinationProvide = deleteGroupStreamingDestinationsQuery;
+ [itemProvide] = mockHttpTypeDestination;
});
it('emits "deleting" event when busy deleting', () => {
- createComponent();
+ createComponent(deleteStreamingSuccess);
clickDeleteFramework();
expect(wrapper.emitted('deleting')).toHaveLength(1);
});
it('calls the delete mutation with the destination ID', async () => {
- createComponent(deleteAmazonS3Success);
+ createComponent(deleteStreamingSuccess);
clickDeleteFramework();
await waitForPromises();
- expect(deleteAmazonS3Success).toHaveBeenCalledWith({
- id: mockAmazonS3Destinations[0].id,
+ expect(deleteStreamingSuccess).toHaveBeenCalledWith({
+ id: mockHttpTypeDestination[0].id,
isInstance: false,
});
});
it('emits "delete" event when the destination is successfully deleted', async () => {
- createComponent(deleteAmazonS3Success);
+ createComponent(deleteStreamingSuccess);
clickDeleteFramework();
await waitForPromises();
@@ -314,7 +133,7 @@ describe('StreamDeleteModal', () => {
});
it('emits "error" event when there is a network error', async () => {
- createComponent(deleteNetworkError);
+ createComponent(deleteStreamingNetworkError);
clickDeleteFramework();
await waitForPromises();
@@ -323,7 +142,7 @@ describe('StreamDeleteModal', () => {
});
it('emits "error" event when there is a graphql error', async () => {
- createComponent(deleteError);
+ createComponent(deleteStreamingError);
clickDeleteFramework();
await waitForPromises();
@@ -335,32 +154,31 @@ describe('StreamDeleteModal', () => {
describe('Instance clickDeleteDestination', () => {
beforeEach(() => {
groupPathProvide = instanceGroupPath;
- itemProvide = instanceDestination;
- typeProvide = instanceGroupPath;
- deleteExternalDestinationProvide = deleteInstanceExternalDestination;
+ deleteExternalDestinationProvide = deleteInstanceStreamingDestinationsQuery;
+ [itemProvide] = mockHttpTypeDestination;
});
it('emits "deleting" event when busy deleting', () => {
- createComponent();
+ createComponent(deleteInstanceStreamingSuccess);
clickDeleteFramework();
expect(wrapper.emitted('deleting')).toHaveLength(1);
});
it('calls the delete mutation with the destination ID', async () => {
- createComponent(deleteInstanceSuccess);
+ createComponent(deleteInstanceStreamingSuccess);
clickDeleteFramework();
await waitForPromises();
- expect(deleteInstanceSuccess).toHaveBeenCalledWith({
- id: mockExternalDestinations[0].id,
+ expect(deleteInstanceStreamingSuccess).toHaveBeenCalledWith({
+ id: mockHttpTypeDestination[0].id,
isInstance: true,
});
});
it('emits "delete" event when the destination is successfully deleted', async () => {
- createComponent(deleteInstanceSuccess);
+ createComponent(deleteInstanceStreamingSuccess);
clickDeleteFramework();
await waitForPromises();
@@ -369,7 +187,7 @@ describe('StreamDeleteModal', () => {
});
it('emits "error" event when there is a network error', async () => {
- createComponent(deleteNetworkError);
+ createComponent(deleteInstanceStreamingNetworkError);
clickDeleteFramework();
await waitForPromises();
@@ -378,7 +196,7 @@ describe('StreamDeleteModal', () => {
});
it('emits "error" event when there is a graphql error', async () => {
- createComponent(deleteError);
+ createComponent(deleteInstanceStreamingError);
clickDeleteFramework();
await waitForPromises();
@@ -386,110 +204,4 @@ describe('StreamDeleteModal', () => {
expect(wrapper.emitted('error')).toHaveLength(1);
});
});
-
- describe('Instance GCP Logging clickDeleteDestination', () => {
- beforeEach(() => {
- groupPathProvide = instanceGroupPath;
- itemProvide = instanceGcpLoggingDestination;
- typeProvide = mockGcpLoggingType;
- deleteExternalDestinationProvide = instanceGoogleCloudLoggingConfigurationDestroy;
- });
-
- it('emits "deleting" event when busy deleting', () => {
- createComponent();
- clickDeleteFramework();
-
- expect(wrapper.emitted('deleting')).toHaveLength(1);
- });
-
- it('calls the delete mutation with the destination ID', async () => {
- createComponent(deleteInstanceGcpLoggingSuccess);
- clickDeleteFramework();
-
- await waitForPromises();
-
- expect(deleteInstanceGcpLoggingSuccess).toHaveBeenCalledWith({
- id: mockInstanceGcpLoggingDestinations[0].id,
- isInstance: true,
- });
- });
-
- it('emits "delete" event when the destination is successfully deleted', async () => {
- createComponent(deleteInstanceGcpLoggingSuccess);
- clickDeleteFramework();
-
- await waitForPromises();
-
- expect(wrapper.emitted('delete')).toHaveLength(1);
- });
-
- it('emits "error" event when there is a network error', async () => {
- createComponent(deleteNetworkError);
- clickDeleteFramework();
-
- await waitForPromises();
-
- expect(wrapper.emitted('error')).toHaveLength(1);
- });
-
- it('emits "error" event when there is a graphql error', async () => {
- createComponent(deleteError);
- clickDeleteFramework();
-
- await waitForPromises();
-
- expect(wrapper.emitted('error')).toHaveLength(1);
- });
- });
-
- describe('Instance Amazon S3 clickDeleteDestination', () => {
- beforeEach(() => {
- groupPathProvide = instanceGroupPath;
- itemProvide = instanceAmazonS3Destination;
- typeProvide = mockAmazonS3Type;
- deleteExternalDestinationProvide = instanceAmazonS3ConfigurationDestroy;
- });
-
- it('emits "deleting" event when busy deleting', () => {
- createComponent();
- clickDeleteFramework();
-
- expect(wrapper.emitted('deleting')).toHaveLength(1);
- });
-
- describe('when deleting', () => {
- beforeEach(async () => {
- createComponent(deleteInstanceAmazonS3Success);
- clickDeleteFramework();
-
- await waitForPromises();
- });
- it('calls the delete mutation with the destination ID', () => {
- expect(deleteInstanceAmazonS3Success).toHaveBeenCalledWith({
- id: mockInstanceAmazonS3Destinations[0].id,
- isInstance: true,
- });
- });
-
- it('emits "delete" event when the destination is successfully deleted', () => {
- expect(wrapper.emitted('delete')).toHaveLength(1);
- });
- });
-
- describe.each([
- ['network error', deleteNetworkError],
- ['graphql error', deleteError],
- ])('when %p', (_, mockResolver) => {
- beforeEach(async () => {
- createComponent(mockResolver);
- clickDeleteFramework();
-
- await waitForPromises();
- });
-
- it('emits "error" event', () => {
- expect(wrapper.emitted('error')).toHaveLength(1);
- });
- });
- });
});
diff --git a/ee/spec/frontend/audit_events/components/stream/stream_gcp_logging_destination_editor_spec.js b/ee/spec/frontend/audit_events/components/stream/stream_gcp_logging_destination_editor_spec.js
deleted file mode 100644
index 03b27dff8ac403acd31720269a90bfdac1e36dd3..0000000000000000000000000000000000000000
--- a/ee/spec/frontend/audit_events/components/stream/stream_gcp_logging_destination_editor_spec.js
+++ /dev/null
@@ -1,861 +0,0 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import { GlForm } from '@gitlab/ui';
-import * as Sentry from '~/sentry/sentry_browser_wrapper';
-import { createAlert } from '~/alert';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import googleCloudLoggingConfigurationCreate from 'ee/audit_events/graphql/mutations/create_gcp_logging_destination.mutation.graphql';
-import googleCloudLoggingConfigurationUpdate from 'ee/audit_events/graphql/mutations/update_gcp_logging_destination.mutation.graphql';
-import instanceGoogleCloudLoggingConfigurationCreate from 'ee/audit_events/graphql/mutations/create_instance_gcp_logging_destination.mutation.graphql';
-import instanceGoogleCloudLoggingConfigurationUpdate from 'ee/audit_events/graphql/mutations/update_instance_gcp_logging_destination.mutation.graphql';
-import StreamGcpLoggingDestinationEditor from 'ee/audit_events/components/stream/stream_gcp_logging_destination_editor.vue';
-import StreamDeleteModal from 'ee/audit_events/components/stream/stream_delete_modal.vue';
-import { AUDIT_STREAMS_NETWORK_ERRORS, ADD_STREAM_EDITOR_I18N } from 'ee/audit_events/constants';
-import {
- gcpLoggingDestinationCreateMutationPopulator,
- gcpLoggingDestinationUpdateMutationPopulator,
- groupPath,
- mockGcpLoggingDestinations,
- instanceGroupPath,
- mockInstanceGcpLoggingDestinations,
- instanceGcpLoggingDestinationCreateMutationPopulator,
- instanceGcpLoggingDestinationUpdateMutationPopulator,
-} from '../../mock_data';
-import { mockGcpTypeDestination } from '../../mock_data/consolidated_api';
-
-jest.mock('~/alert');
-Vue.use(VueApollo);
-
-describe('StreamGcpLoggingDestinationEditor', () => {
- let wrapper;
- let groupPathProvide = groupPath;
-
- const createComponent = ({
- mountFn = mountExtended,
- props = {},
- provide = {},
- apolloHandlers = [
- [
- googleCloudLoggingConfigurationCreate,
- jest.fn().mockResolvedValue(gcpLoggingDestinationCreateMutationPopulator()),
- ],
- ],
- } = {}) => {
- const mockApollo = createMockApollo(apolloHandlers);
- wrapper = mountFn(StreamGcpLoggingDestinationEditor, {
- attachTo: document.body,
- provide: {
- groupPath: groupPathProvide,
- ...provide,
- },
- propsData: {
- ...props,
- },
- apolloProvider: mockApollo,
- });
- };
-
- const findWarningMessage = () => wrapper.findByTestId('data-warning');
- const findAlertErrors = () => wrapper.findAllByTestId('alert-errors');
- const findDestinationForm = () => wrapper.findComponent(GlForm);
- const findSubmitStreamBtn = () => wrapper.findByTestId('stream-destination-submit-button');
- const findCancelStreamBtn = () => wrapper.findByTestId('stream-destination-cancel-button');
- const findDeleteBtn = () => wrapper.findByTestId('stream-destination-delete-button');
- const findDeleteModal = () => wrapper.findComponent(StreamDeleteModal);
-
- const findNameFormGroup = () => wrapper.findByTestId('name-form-group');
- const findName = () => wrapper.findByTestId('name');
- const findProjectIdFormGroup = () => wrapper.findByTestId('project-id-form-group');
- const findProjectId = () => wrapper.findByTestId('project-id');
- const findClientEmailFormGroup = () => wrapper.findByTestId('client-email-form-group');
- const findClientEmailUrl = () => wrapper.findByTestId('client-email');
- const findLogIdFormGroup = () => wrapper.findByTestId('log-id-form-group');
- const findLogId = () => wrapper.findByTestId('log-id');
- const findPrivateKeyFormGroup = () => wrapper.findByTestId('private-key-form-group');
- const findPrivateKey = () => wrapper.findByTestId('private-key');
- const findPrivateKeyAddButton = () => wrapper.findByTestId('private-key-add-button');
- const findPrivateKeyCancelButton = () => wrapper.findByTestId('private-key-cancel-button');
-
- afterEach(() => {
- createAlert.mockClear();
- });
-
- describe('when useConsolidatedAuditEventStreamDestApi is enabled', () => {
- const item = mockGcpTypeDestination[0];
-
- beforeEach(() => {
- createComponent({
- props: { item },
- provide: {
- glFeatures: { useConsolidatedAuditEventStreamDestApi: true },
- },
- });
- });
-
- it('renders the destination correctly', () => {
- expect(findName().element.value).toBe('GCP Destination 1');
- expect(findProjectId().element.value).toBe('google-project-id-name');
- expect(findClientEmailUrl().element.value).toBe('clientEmail@example.com');
- expect(findLogId().element.value).toBe('gcp-log-id-name');
- expect(findPrivateKey().exists()).toBe(false);
- expect(findPrivateKeyAddButton().exists()).toBe(true);
- expect(findPrivateKeyCancelButton().exists()).toBe(false);
- });
- });
-
- describe('Group GCP Logging stream destination editor', () => {
- describe('when initialized', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('should render the destinations warning', () => {
- expect(findWarningMessage().props('title')).toBe(ADD_STREAM_EDITOR_I18N.WARNING_TITLE);
- });
-
- it('should render the destination name input', () => {
- expect(findNameFormGroup().exists()).toBe(true);
- expect(findName().exists()).toBe(true);
- expect(findName().attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.GCP_LOGGING_DESTINATION_NAME_PLACEHOLDER,
- );
- });
-
- it('should render the destination ProjectId input', () => {
- expect(findProjectIdFormGroup().exists()).toBe(true);
- expect(findProjectId().exists()).toBe(true);
- expect(findProjectId().attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.GCP_LOGGING_DESTINATION_PROJECT_ID_PLACEHOLDER,
- );
- });
-
- it('should render the destination ClientEmail input', () => {
- expect(findClientEmailFormGroup().exists()).toBe(true);
- expect(findClientEmailUrl().exists()).toBe(true);
- expect(findClientEmailUrl().attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.GCP_LOGGING_DESTINATION_CLIENT_EMAIL_PLACEHOLDER,
- );
- });
-
- it('should render the destination IdForm input', () => {
- expect(findLogIdFormGroup().exists()).toBe(true);
- expect(findLogId().exists()).toBe(true);
- expect(findLogId().attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.GCP_LOGGING_DESTINATION_LOG_ID_PLACEHOLDER,
- );
- });
-
- it('should render the destination Private key input', () => {
- expect(findPrivateKeyFormGroup().exists()).toBe(true);
- expect(findPrivateKey().exists()).toBe(true);
- });
-
- it('does not render the delete button', () => {
- expect(findDeleteBtn().exists()).toBe(false);
- });
-
- it('renders the add button text', () => {
- expect(findSubmitStreamBtn().attributes('name')).toBe(
- ADD_STREAM_EDITOR_I18N.ADD_BUTTON_NAME,
- );
- expect(findSubmitStreamBtn().text()).toBe(ADD_STREAM_EDITOR_I18N.ADD_BUTTON_TEXT);
- });
-
- it('disables the add button at first', () => {
- expect(findSubmitStreamBtn().props('disabled')).toBe(true);
- });
- });
-
- describe('add destination event', () => {
- it('should emit add event after destination added', async () => {
- createComponent();
-
- await findName().setValue(mockGcpLoggingDestinations[0].name);
- await findProjectId().setValue(mockGcpLoggingDestinations[0].googleProjectIdName);
- await findClientEmailUrl().setValue(mockGcpLoggingDestinations[0].clientEmail);
- await findLogId().setValue(mockGcpLoggingDestinations[0].logIdName);
- await findPrivateKey().setValue(mockGcpLoggingDestinations[0].privateKey);
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(false);
-
- await findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('added')).toBeDefined();
- });
-
- it('should not emit add destination event and reports error when server returns error', async () => {
- const errorMsg = 'Destination hosts limit exceeded';
- createComponent({
- apolloHandlers: [
- [
- googleCloudLoggingConfigurationCreate,
- jest.fn().mockResolvedValue(gcpLoggingDestinationCreateMutationPopulator([errorMsg])),
- ],
- ],
- });
-
- findName().setValue(mockGcpLoggingDestinations[0].name);
- findProjectId().setValue(mockGcpLoggingDestinations[0].googleProjectIdName);
- findClientEmailUrl().setValue(mockGcpLoggingDestinations[0].clientEmail);
- findLogId().setValue(mockGcpLoggingDestinations[0].logIdName);
- findPrivateKey().setValue(mockGcpLoggingDestinations[0].privateKey);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(errorMsg);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('added')).toBeUndefined();
- });
-
- it('should not emit add destination event and reports error when network error occurs', async () => {
- const sentryError = new Error('Network error');
- const sentryCaptureExceptionSpy = jest.spyOn(Sentry, 'captureException');
- createComponent({
- apolloHandlers: [
- [googleCloudLoggingConfigurationCreate, jest.fn().mockRejectedValue(sentryError)],
- ],
- });
-
- findName().setValue(mockGcpLoggingDestinations[0].name);
- findProjectId().setValue(mockGcpLoggingDestinations[0].googleProjectIdName);
- findClientEmailUrl().setValue(mockGcpLoggingDestinations[0].clientEmail);
- findLogId().setValue(mockGcpLoggingDestinations[0].logIdName);
- findPrivateKey().setValue(mockGcpLoggingDestinations[0].privateKey);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(AUDIT_STREAMS_NETWORK_ERRORS.CREATING_ERROR);
- expect(sentryCaptureExceptionSpy).toHaveBeenCalledWith(sentryError);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('added')).toBeUndefined();
- });
- });
-
- describe('cancel event', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('should emit cancel event correctly', () => {
- findCancelStreamBtn().vm.$emit('click');
-
- expect(wrapper.emitted('cancel')).toBeDefined();
- });
- });
-
- describe('when editing an existing destination', () => {
- describe('renders', () => {
- beforeEach(() => {
- createComponent({ props: { item: mockGcpLoggingDestinations[0] } });
- });
-
- it('the destination fields', () => {
- expect(findName().element.value).toBe(mockGcpLoggingDestinations[0].name);
- expect(findProjectId().element.value).toBe(
- mockGcpLoggingDestinations[0].googleProjectIdName,
- );
- expect(findClientEmailUrl().element.value).toBe(
- mockGcpLoggingDestinations[0].clientEmail,
- );
- expect(findLogId().element.value).toBe(mockGcpLoggingDestinations[0].logIdName);
- expect(findPrivateKey().exists()).toBe(false);
- expect(findPrivateKeyAddButton().exists()).toBe(true);
- expect(findPrivateKeyCancelButton().exists()).toBe(false);
- });
-
- it('the delete button', () => {
- expect(findDeleteBtn().exists()).toBe(true);
- });
-
- it('renders the save button text', () => {
- expect(findSubmitStreamBtn().attributes('name')).toBe(
- ADD_STREAM_EDITOR_I18N.SAVE_BUTTON_NAME,
- );
- expect(findSubmitStreamBtn().text()).toBe(ADD_STREAM_EDITOR_I18N.SAVE_BUTTON_TEXT);
- });
-
- it('disables the save button at first', () => {
- expect(findSubmitStreamBtn().props('disabled')).toBe(true);
- });
-
- it('displays the private key field when adding', async () => {
- await findPrivateKeyAddButton().trigger('click');
-
- expect(findPrivateKeyAddButton().props('disabled')).toBe(true);
- expect(findPrivateKeyCancelButton().exists()).toBe(true);
- expect(findPrivateKey().element.value).toBe('');
- });
-
- it('removes the private key field when cancelled', async () => {
- await findPrivateKeyAddButton().trigger('click');
- await findPrivateKeyCancelButton().trigger('click');
-
- expect(findPrivateKeyAddButton().props('disabled')).toBe(false);
- expect(findPrivateKey().exists()).toBe(false);
- expect(findPrivateKeyAddButton().exists()).toBe(true);
- expect(findPrivateKeyCancelButton().exists()).toBe(false);
- });
- });
-
- it.each`
- name | findInputFn
- ${'Destination Name'} | ${findName}
- ${'Project ID'} | ${findProjectId}
- ${'Client Email'} | ${findClientEmailUrl}
- ${'Log ID'} | ${findLogId}
- `('enable the save button when $name is edited', async ({ findInputFn }) => {
- createComponent({ props: { item: mockGcpLoggingDestinations[0] } });
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(true);
-
- await findInputFn().setValue('test');
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(false);
- });
-
- it('should emit updated event after destination updated', async () => {
- createComponent({
- props: { item: mockGcpLoggingDestinations[0] },
- apolloHandlers: [
- [
- googleCloudLoggingConfigurationUpdate,
- jest.fn().mockResolvedValue(gcpLoggingDestinationUpdateMutationPopulator()),
- ],
- ],
- });
-
- findName().setValue(mockGcpLoggingDestinations[0].name);
- findProjectId().setValue(mockGcpLoggingDestinations[1].googleProjectIdName);
- findClientEmailUrl().setValue(mockGcpLoggingDestinations[1].clientEmail);
- findLogId().setValue(mockGcpLoggingDestinations[1].logIdName);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('updated')).toBeDefined();
- });
-
- it('should emit updated event after destination private key updated', async () => {
- createComponent({
- props: { item: mockGcpLoggingDestinations[0] },
- apolloHandlers: [
- [
- googleCloudLoggingConfigurationUpdate,
- jest.fn().mockResolvedValue(gcpLoggingDestinationUpdateMutationPopulator()),
- ],
- ],
- });
-
- await findPrivateKeyAddButton().trigger('click');
-
- findPrivateKey().setValue(mockGcpLoggingDestinations[1].privateKey);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('updated')).toBeDefined();
- });
-
- it('should not emit add destination event and reports error when server returns error', async () => {
- const errorMsg = 'Destination hosts limit exceeded';
- createComponent({
- props: { item: mockGcpLoggingDestinations[0] },
- apolloHandlers: [
- [
- googleCloudLoggingConfigurationUpdate,
- jest.fn().mockResolvedValue(gcpLoggingDestinationUpdateMutationPopulator([errorMsg])),
- ],
- ],
- });
-
- findName().setValue(mockGcpLoggingDestinations[0].name);
- findProjectId().setValue(mockGcpLoggingDestinations[0].googleProjectIdName);
- findClientEmailUrl().setValue(mockGcpLoggingDestinations[0].clientEmail);
- findLogId().setValue(mockGcpLoggingDestinations[0].logIdName);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(errorMsg);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('updated')).toBeUndefined();
- });
-
- it('should not emit add destination event and reports error when network error occurs', async () => {
- const sentryError = new Error('Network error');
- const sentryCaptureExceptionSpy = jest.spyOn(Sentry, 'captureException');
- createComponent({
- props: { item: mockGcpLoggingDestinations[0] },
- apolloHandlers: [
- [googleCloudLoggingConfigurationUpdate, jest.fn().mockRejectedValue(sentryError)],
- ],
- });
-
- findName().setValue(mockGcpLoggingDestinations[0].name);
- findProjectId().setValue(mockGcpLoggingDestinations[0].googleProjectIdName);
- findClientEmailUrl().setValue(mockGcpLoggingDestinations[0].clientEmail);
- findLogId().setValue(mockGcpLoggingDestinations[0].logIdName);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(AUDIT_STREAMS_NETWORK_ERRORS.UPDATING_ERROR);
- expect(sentryCaptureExceptionSpy).toHaveBeenCalledWith(sentryError);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('updated')).toBeUndefined();
- });
- });
-
- describe('deleting', () => {
- beforeEach(() => {
- createComponent({ props: { item: mockGcpLoggingDestinations[0] } });
- });
-
- it('should emit deleted on success operation', async () => {
- const deleteButton = findDeleteBtn();
- await deleteButton.trigger('click');
- await findDeleteModal().vm.$emit('deleting');
-
- expect(deleteButton.props('loading')).toBe(true);
-
- await findDeleteModal().vm.$emit('delete');
-
- expect(deleteButton.props('loading')).toBe(false);
- expect(wrapper.emitted('deleted')).toEqual([[mockGcpLoggingDestinations[0].id]]);
- });
-
- it('shows the alert for the error', () => {
- const errorMsg = 'An error occurred';
- findDeleteModal().vm.$emit('error', errorMsg);
-
- expect(createAlert).toHaveBeenCalledWith({
- message: AUDIT_STREAMS_NETWORK_ERRORS.DELETING_ERROR,
- captureError: true,
- error: errorMsg,
- });
- });
- });
-
- it('passes actual newlines when these are used in the private key input', async () => {
- const mutationMock = jest
- .fn()
- .mockResolvedValue(gcpLoggingDestinationCreateMutationPopulator());
- createComponent({
- apolloHandlers: [[googleCloudLoggingConfigurationCreate, mutationMock]],
- });
-
- await findPrivateKey().setValue('\\ntest\\n');
- await findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
-
- expect(mutationMock).toHaveBeenCalledWith(
- expect.objectContaining({
- privateKey: '\ntest\n',
- }),
- );
- });
- });
-
- describe('Instance GCP Logging stream destination editor', () => {
- beforeEach(() => {
- groupPathProvide = instanceGroupPath;
- });
-
- describe('when initialized', () => {
- beforeEach(() => {
- createComponent({
- apolloHandlers: [
- [
- instanceGoogleCloudLoggingConfigurationCreate,
- jest.fn().mockResolvedValue(instanceGcpLoggingDestinationCreateMutationPopulator()),
- ],
- ],
- });
- });
-
- it('should render the destinations warning', () => {
- expect(findWarningMessage().props('title')).toBe(ADD_STREAM_EDITOR_I18N.WARNING_TITLE);
- });
-
- it('should render the destination name input', () => {
- expect(findNameFormGroup().exists()).toBe(true);
- expect(findName().exists()).toBe(true);
- expect(findName().attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.GCP_LOGGING_DESTINATION_NAME_PLACEHOLDER,
- );
- });
-
- it('should render the destination ProjectId input', () => {
- expect(findProjectIdFormGroup().exists()).toBe(true);
- expect(findProjectId().exists()).toBe(true);
- expect(findProjectId().attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.GCP_LOGGING_DESTINATION_PROJECT_ID_PLACEHOLDER,
- );
- });
-
- it('should render the destination ClientEmail input', () => {
- expect(findClientEmailFormGroup().exists()).toBe(true);
- expect(findClientEmailUrl().exists()).toBe(true);
- expect(findClientEmailUrl().attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.GCP_LOGGING_DESTINATION_CLIENT_EMAIL_PLACEHOLDER,
- );
- });
-
- it('should render the destination IdForm input', () => {
- expect(findLogIdFormGroup().exists()).toBe(true);
- expect(findLogId().exists()).toBe(true);
- expect(findLogId().attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.GCP_LOGGING_DESTINATION_LOG_ID_PLACEHOLDER,
- );
- });
-
- it('should render the destination Private key input', () => {
- expect(findPrivateKeyFormGroup().exists()).toBe(true);
- expect(findPrivateKey().exists()).toBe(true);
- });
-
- it('does not render the delete button', () => {
- expect(findDeleteBtn().exists()).toBe(false);
- });
-
- it('renders the add button text', () => {
- expect(findSubmitStreamBtn().attributes('name')).toBe(
- ADD_STREAM_EDITOR_I18N.ADD_BUTTON_NAME,
- );
- expect(findSubmitStreamBtn().text()).toBe(ADD_STREAM_EDITOR_I18N.ADD_BUTTON_TEXT);
- });
-
- it('disables the add button at first', () => {
- expect(findSubmitStreamBtn().props('disabled')).toBe(true);
- });
- });
-
- describe('add destination event', () => {
- it('should emit add event after destination added', async () => {
- createComponent({
- apolloHandlers: [
- [
- instanceGoogleCloudLoggingConfigurationCreate,
- jest.fn().mockResolvedValue(instanceGcpLoggingDestinationCreateMutationPopulator()),
- ],
- ],
- });
-
- await findName().setValue(mockInstanceGcpLoggingDestinations[0].name);
- await findProjectId().setValue(mockInstanceGcpLoggingDestinations[0].googleProjectIdName);
- await findClientEmailUrl().setValue(mockInstanceGcpLoggingDestinations[0].clientEmail);
- await findLogId().setValue(mockInstanceGcpLoggingDestinations[0].logIdName);
- await findPrivateKey().setValue(mockInstanceGcpLoggingDestinations[0].privateKey);
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(false);
-
- await findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('added')).toBeDefined();
- });
-
- it('should not emit add destination event and reports error when server returns error', async () => {
- const errorMsg = 'Destination hosts limit exceeded';
- createComponent({
- apolloHandlers: [
- [
- instanceGoogleCloudLoggingConfigurationCreate,
- jest
- .fn()
- .mockResolvedValue(
- instanceGcpLoggingDestinationCreateMutationPopulator([errorMsg]),
- ),
- ],
- ],
- });
-
- findName().setValue(mockInstanceGcpLoggingDestinations[0].name);
- findProjectId().setValue(mockInstanceGcpLoggingDestinations[0].googleProjectIdName);
- findClientEmailUrl().setValue(mockInstanceGcpLoggingDestinations[0].clientEmail);
- findLogId().setValue(mockInstanceGcpLoggingDestinations[0].logIdName);
- findPrivateKey().setValue(mockInstanceGcpLoggingDestinations[0].privateKey);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(errorMsg);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('added')).toBeUndefined();
- });
-
- it('should not emit add destination event and reports error when network error occurs', async () => {
- const sentryError = new Error('Network error');
- const sentryCaptureExceptionSpy = jest.spyOn(Sentry, 'captureException');
- createComponent({
- apolloHandlers: [
- [
- instanceGoogleCloudLoggingConfigurationCreate,
- jest.fn().mockRejectedValue(sentryError),
- ],
- ],
- });
-
- findName().setValue(mockInstanceGcpLoggingDestinations[0].name);
- findProjectId().setValue(mockInstanceGcpLoggingDestinations[0].googleProjectIdName);
- findClientEmailUrl().setValue(mockInstanceGcpLoggingDestinations[0].clientEmail);
- findLogId().setValue(mockInstanceGcpLoggingDestinations[0].logIdName);
- findPrivateKey().setValue(mockInstanceGcpLoggingDestinations[0].privateKey);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(AUDIT_STREAMS_NETWORK_ERRORS.CREATING_ERROR);
- expect(sentryCaptureExceptionSpy).toHaveBeenCalledWith(sentryError);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('added')).toBeUndefined();
- });
- });
-
- describe('cancel event', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('should emit cancel event correctly', () => {
- findCancelStreamBtn().vm.$emit('click');
-
- expect(wrapper.emitted('cancel')).toBeDefined();
- });
- });
-
- describe('when editing an existing destination', () => {
- describe('renders', () => {
- beforeEach(() => {
- createComponent({ props: { item: mockInstanceGcpLoggingDestinations[0] } });
- });
-
- it('the destination fields', () => {
- expect(findName().exists()).toBe(true);
- expect(findName().element.value).toBe(mockInstanceGcpLoggingDestinations[0].name);
- expect(findProjectId().exists()).toBe(true);
- expect(findProjectId().element.value).toBe(
- mockInstanceGcpLoggingDestinations[0].googleProjectIdName,
- );
- expect(findClientEmailUrl().exists()).toBe(true);
- expect(findClientEmailUrl().element.value).toBe(
- mockInstanceGcpLoggingDestinations[0].clientEmail,
- );
- expect(findLogId().exists()).toBe(true);
- expect(findLogId().element.value).toBe(mockInstanceGcpLoggingDestinations[0].logIdName);
- expect(findPrivateKey().exists()).toBe(false);
- expect(findPrivateKeyAddButton().exists()).toBe(true);
- expect(findPrivateKeyCancelButton().exists()).toBe(false);
- });
-
- it('the delete button', () => {
- expect(findDeleteBtn().exists()).toBe(true);
- });
-
- it('renders the save button text', () => {
- expect(findSubmitStreamBtn().attributes('name')).toBe(
- ADD_STREAM_EDITOR_I18N.SAVE_BUTTON_NAME,
- );
- expect(findSubmitStreamBtn().text()).toBe(ADD_STREAM_EDITOR_I18N.SAVE_BUTTON_TEXT);
- });
-
- it('disables the save button at first', () => {
- expect(findSubmitStreamBtn().props('disabled')).toBe(true);
- });
-
- it('displays the private key field when adding', async () => {
- await findPrivateKeyAddButton().trigger('click');
-
- expect(findPrivateKeyAddButton().props('disabled')).toBe(true);
- expect(findPrivateKeyCancelButton().exists()).toBe(true);
- expect(findPrivateKey().element.value).toBe('');
- });
-
- it('removes the private key field when cancelled', async () => {
- await findPrivateKeyAddButton().trigger('click');
- await findPrivateKeyCancelButton().trigger('click');
-
- expect(findPrivateKeyAddButton().props('disabled')).toBe(false);
- expect(findPrivateKey().exists()).toBe(false);
- expect(findPrivateKeyAddButton().exists()).toBe(true);
- expect(findPrivateKeyCancelButton().exists()).toBe(false);
- });
- });
-
- it.each`
- name | findInputFn
- ${'Destination Name'} | ${findName}
- ${'Project ID'} | ${findProjectId}
- ${'Client Email'} | ${findClientEmailUrl}
- ${'Log ID'} | ${findLogId}
- `('enable the save button when $name is edited', async ({ findInputFn }) => {
- createComponent({ props: { item: mockInstanceGcpLoggingDestinations[0] } });
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(true);
-
- await findInputFn().setValue('test');
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(false);
- });
-
- it('should emit updated event after destination updated', async () => {
- createComponent({
- props: { item: mockInstanceGcpLoggingDestinations[0] },
- apolloHandlers: [
- [
- instanceGoogleCloudLoggingConfigurationUpdate,
- jest.fn().mockResolvedValue(instanceGcpLoggingDestinationUpdateMutationPopulator()),
- ],
- ],
- });
-
- findName().setValue(mockInstanceGcpLoggingDestinations[0].name);
- findProjectId().setValue(mockInstanceGcpLoggingDestinations[1].googleProjectIdName);
- findClientEmailUrl().setValue(mockInstanceGcpLoggingDestinations[1].clientEmail);
- findLogId().setValue(mockInstanceGcpLoggingDestinations[1].logIdName);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('updated')).toBeDefined();
- });
-
- it('should emit updated event after destination private key updated', async () => {
- createComponent({
- props: { item: mockInstanceGcpLoggingDestinations[0] },
- apolloHandlers: [
- [
- instanceGoogleCloudLoggingConfigurationUpdate,
- jest.fn().mockResolvedValue(instanceGcpLoggingDestinationUpdateMutationPopulator()),
- ],
- ],
- });
-
- await findPrivateKeyAddButton().trigger('click');
-
- findPrivateKey().setValue(mockInstanceGcpLoggingDestinations[1].privateKey);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('updated')).toBeDefined();
- });
-
- it('should not emit add destination event and reports error when server returns error', async () => {
- const errorMsg = 'Destination hosts limit exceeded';
- createComponent({
- props: { item: mockInstanceGcpLoggingDestinations[0] },
- apolloHandlers: [
- [
- instanceGoogleCloudLoggingConfigurationUpdate,
- jest
- .fn()
- .mockResolvedValue(
- instanceGcpLoggingDestinationUpdateMutationPopulator([errorMsg]),
- ),
- ],
- ],
- });
-
- findName().setValue(mockInstanceGcpLoggingDestinations[0].name);
- findProjectId().setValue(mockInstanceGcpLoggingDestinations[0].googleProjectIdName);
- findClientEmailUrl().setValue(mockInstanceGcpLoggingDestinations[0].clientEmail);
- findLogId().setValue(mockInstanceGcpLoggingDestinations[0].logIdName);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(errorMsg);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('updated')).toBeUndefined();
- });
-
- it('should not emit add destination event and reports error when network error occurs', async () => {
- const sentryError = new Error('Network error');
- const sentryCaptureExceptionSpy = jest.spyOn(Sentry, 'captureException');
- createComponent({
- props: { item: mockInstanceGcpLoggingDestinations[0] },
- apolloHandlers: [
- [
- instanceGoogleCloudLoggingConfigurationUpdate,
- jest.fn().mockRejectedValue(sentryError),
- ],
- ],
- });
-
- findName().setValue(mockInstanceGcpLoggingDestinations[0].name);
- findProjectId().setValue(mockInstanceGcpLoggingDestinations[0].googleProjectIdName);
- findClientEmailUrl().setValue(mockInstanceGcpLoggingDestinations[0].clientEmail);
- findLogId().setValue(mockInstanceGcpLoggingDestinations[0].logIdName);
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(AUDIT_STREAMS_NETWORK_ERRORS.UPDATING_ERROR);
- expect(sentryCaptureExceptionSpy).toHaveBeenCalledWith(sentryError);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('updated')).toBeUndefined();
- });
- });
-
- describe('deleting', () => {
- beforeEach(() => {
- createComponent({ props: { item: mockInstanceGcpLoggingDestinations[0] } });
- });
-
- it('should emit deleted on success operation', async () => {
- const deleteButton = findDeleteBtn();
- await deleteButton.trigger('click');
- await findDeleteModal().vm.$emit('deleting');
-
- expect(deleteButton.props('loading')).toBe(true);
-
- await findDeleteModal().vm.$emit('delete');
-
- expect(deleteButton.props('loading')).toBe(false);
- expect(wrapper.emitted('deleted')).toEqual([[mockInstanceGcpLoggingDestinations[0].id]]);
- });
-
- it('shows the alert for the error', () => {
- const errorMsg = 'An error occurred';
- findDeleteModal().vm.$emit('error', errorMsg);
-
- expect(createAlert).toHaveBeenCalledWith({
- message: AUDIT_STREAMS_NETWORK_ERRORS.DELETING_ERROR,
- captureError: true,
- error: errorMsg,
- });
- });
- });
-
- it('passes actual newlines when these are used in the private key input', async () => {
- const mutationMock = jest
- .fn()
- .mockResolvedValue(instanceGcpLoggingDestinationCreateMutationPopulator());
- createComponent({
- apolloHandlers: [[instanceGoogleCloudLoggingConfigurationCreate, mutationMock]],
- });
-
- await findPrivateKey().setValue('\\ntest\\n');
- await findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
-
- expect(mutationMock).toHaveBeenCalledWith(
- expect.objectContaining({
- privateKey: '\ntest\n',
- }),
- );
- });
- });
-});
diff --git a/ee/spec/frontend/audit_events/components/stream/stream_http_destination_editor_spec.js b/ee/spec/frontend/audit_events/components/stream/stream_http_destination_editor_spec.js
deleted file mode 100644
index 5aecf60b2d077204687d2a512625f6b813a82746..0000000000000000000000000000000000000000
--- a/ee/spec/frontend/audit_events/components/stream/stream_http_destination_editor_spec.js
+++ /dev/null
@@ -1,1762 +0,0 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import { GlButton, GlFormCheckbox, GlForm, GlTableLite } from '@gitlab/ui';
-import * as Sentry from '~/sentry/sentry_browser_wrapper';
-import { sprintf } from '~/locale';
-import { createAlert } from '~/alert';
-import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { mountExtended, extendedWrapper } from 'helpers/vue_test_utils_helper';
-import { removeAuditEventsStreamingDestinationFromCache } from 'ee/audit_events/graphql/cache_update_consolidated_api';
-import { removeLegacyAuditEventsStreamingDestination } from 'ee/audit_events/graphql/cache_update';
-import getNamespaceFiltersQuery from 'ee/audit_events/graphql/queries/get_namespace_filters.query.graphql';
-import externalAuditEventDestinationCreate from 'ee/audit_events/graphql/mutations/create_external_destination.mutation.graphql';
-import externalAuditEventDestinationUpdate from 'ee/audit_events/graphql/mutations/update_external_destination.mutation.graphql';
-import externalAuditEventDestinationHeaderCreate from 'ee/audit_events/graphql/mutations/create_external_destination_header.mutation.graphql';
-import externalAuditEventDestinationHeaderUpdate from 'ee/audit_events/graphql/mutations/update_external_destination_header.mutation.graphql';
-import externalAuditEventDestinationHeaderDelete from 'ee/audit_events/graphql/mutations/delete_external_destination_header.mutation.graphql';
-import deleteExternalDestination from 'ee/audit_events/graphql/mutations/delete_external_destination.mutation.graphql';
-import deleteExternalDestinationFilters from 'ee/audit_events/graphql/mutations/delete_external_destination_filters.mutation.graphql';
-import addExternalDestinationFilters from 'ee/audit_events/graphql/mutations/add_external_destination_filters.mutation.graphql';
-import instanceExternalAuditEventDestinationCreate from 'ee/audit_events/graphql/mutations/create_instance_external_destination.mutation.graphql';
-import instanceExternalAuditEventDestinationUpdate from 'ee/audit_events/graphql/mutations/update_instance_external_destination.mutation.graphql';
-import deleteInstanceExternalDestination from 'ee/audit_events/graphql/mutations/delete_instance_external_destination.mutation.graphql';
-import externalInstanceAuditEventDestinationHeaderCreate from 'ee/audit_events/graphql/mutations/create_instance_external_destination_header.mutation.graphql';
-import externalInstanceAuditEventDestinationHeaderUpdate from 'ee/audit_events/graphql/mutations/update_instance_external_destination_header.mutation.graphql';
-import externalInstanceAuditEventDestinationHeaderDelete from 'ee/audit_events/graphql/mutations/delete_instance_external_destination_header.mutation.graphql';
-import deleteInstanceExternalDestinationFilters from 'ee/audit_events/graphql/mutations/delete_instance_external_destination_filters.mutation.graphql';
-import addInstanceExternalDestinationFilters from 'ee/audit_events/graphql/mutations/add_instance_external_destination_filters.mutation.graphql';
-import addExternalDestinationNamespaceFilters from 'ee/audit_events/graphql/mutations/add_external_destination_namespace_filters.mutation.graphql';
-import deleteExternalDestinationNamespaceFilters from 'ee/audit_events/graphql/mutations/delete_external_destination_namespace_filters.mutation.graphql';
-import StreamHttpDestinationEditor from 'ee/audit_events/components/stream/stream_http_destination_editor.vue';
-import StreamEventTypeFilters from 'ee/audit_events/components/stream/stream_event_type_filters.vue';
-import StreamNamespaceFilters from 'ee/audit_events/components/stream//stream_namespace_filters.vue';
-import StreamDeleteModal from 'ee/audit_events/components/stream/stream_delete_modal.vue';
-import { AUDIT_STREAMS_NETWORK_ERRORS, ADD_STREAM_EDITOR_I18N } from 'ee/audit_events/constants';
-import {
- destinationCreateMutationPopulator,
- destinationDeleteMutationPopulator,
- destinationUpdateMutationPopulator,
- destinationHeaderCreateMutationPopulator,
- destinationHeaderUpdateMutationPopulator,
- destinationHeaderDeleteMutationPopulator,
- groupPath,
- mockExternalDestinations,
- mockInstanceExternalDestinations,
- mockExternalDestinationHeader,
- destinationFilterRemoveMutationPopulator,
- destinationFilterUpdateMutationPopulator,
- mockAuditEventDefinitions,
- mockRemoveFilterSelect,
- mockRemoveFilterRemaining,
- mockAddFilterSelect,
- mockAddFilterRemaining,
- instanceGroupPath,
- mockInstanceExternalDestinationHeader,
- destinationInstanceCreateMutationPopulator,
- destinationInstanceDeleteMutationPopulator,
- destinationInstanceHeaderCreateMutationPopulator,
- destinationInstanceHeaderUpdateMutationPopulator,
- destinationInstanceHeaderDeleteMutationPopulator,
- destinationInstanceFilterRemoveMutationPopulator,
- destinationInstanceFilterUpdateMutationPopulator,
- destinationInstanceUpdateMutationPopulator,
- mockNamespaceFilter,
- destinationNamespaceFilterRemoveMutationPopulator,
- destinationNamespaceFilterAddMutationPopulator,
- mockAddNamespaceFilters,
- mockRemoveNamespaceFilters,
- getMockNamespaceFilters,
-} from '../../mock_data';
-import { mockHttpTypeDestination } from '../../mock_data/consolidated_api';
-
-jest.mock('~/alert');
-jest.mock('ee/audit_events/graphql/cache_update');
-jest.mock('ee/audit_events/graphql/cache_update_consolidated_api');
-
-Vue.use(VueApollo);
-
-describe('StreamHttpDestinationEditor', () => {
- let wrapper;
- let groupPathProvide = groupPath;
-
- const maxHeaders = 3;
- const defaultDeleteSpy = jest.fn().mockResolvedValue(destinationDeleteMutationPopulator());
-
- const createComponent = ({
- mountFn = mountExtended,
- props = {},
- provide = {},
- apolloHandlers = [
- [
- externalAuditEventDestinationCreate,
- jest.fn().mockResolvedValue(destinationCreateMutationPopulator()),
- ],
- ],
- } = {}) => {
- const STATIC_HANDLERS = [
- [getNamespaceFiltersQuery, jest.fn().mockResolvedValue(getMockNamespaceFilters())],
- ];
- const mockApollo = createMockApollo([...apolloHandlers, ...STATIC_HANDLERS]);
- wrapper = mountFn(StreamHttpDestinationEditor, {
- attachTo: document.body,
- provide: {
- groupPath: groupPathProvide,
- maxHeaders,
- auditEventDefinitions: mockAuditEventDefinitions,
- ...provide,
- },
- propsData: {
- ...props,
- },
- apolloProvider: mockApollo,
- });
- };
-
- const findWarningMessage = () => wrapper.findByTestId('data-warning');
- const findAlertErrors = () => wrapper.findAllByTestId('alert-errors');
- const findDestinationForm = () => wrapper.findComponent(GlForm);
- const findHeadersTable = () => wrapper.findComponent(GlTableLite);
- const findNoHeaderCreatedText = () => wrapper.findByTestId('no-header-created');
- const findMaximumHeadersText = () => wrapper.findByTestId('maximum-headers');
- const findAddHeaderBtn = () => wrapper.findByTestId('add-header-row-button');
- const findSubmitStreamBtn = () => wrapper.findByTestId('stream-destination-submit-button');
- const findCancelStreamBtn = () => wrapper.findByTestId('stream-destination-cancel-button');
- const findDeleteBtn = () => wrapper.findByTestId('stream-destination-delete-button');
- const findDeleteModal = () => wrapper.findComponent(StreamDeleteModal);
-
- const findDestinationUrlFormGroup = () => wrapper.findByTestId('destination-url-form-group');
- const findDestinationUrl = () => wrapper.findByTestId('destination-url');
-
- const findDestinationNameFormGroup = () => wrapper.findByTestId('destination-name-form-group');
- const findDestinationName = () => wrapper.findByTestId('destination-name');
-
- const findVerificationTokenFormGroup = () =>
- wrapper.findByTestId('verification-token-form-group');
- const findVerificationToken = () => wrapper.findByTestId('verification-token');
- const findClipboardButton = () => wrapper.findComponent(ClipboardButton);
-
- const findFilteringHeader = () => wrapper.findByTestId('filtering-header');
- const findEventTypeFilteringHeader = () => wrapper.findByTestId('event-type-filtering-header');
- const findFilters = () => wrapper.findComponent(StreamEventTypeFilters);
- const findNamespaceFilters = () => wrapper.findComponent(StreamNamespaceFilters);
- const findNamespaceFilteringHeader = () =>
- wrapper.findByTestId('event-namespace-filtering-header');
- const findHeadersRows = () => findHeadersTable().find('tbody').findAll('tr');
- const findHeadersHeaderCell = (tdIdx) =>
- findHeadersTable().find('thead tr').findAll('th').at(tdIdx);
- const findHeaderCheckbox = (trIdx) => findHeadersRows().at(trIdx).findComponent(GlFormCheckbox);
- const findHeaderDeleteBtn = (trIdx) => findHeadersRows().at(trIdx).findComponent(GlButton);
- const findHeaderNameInput = (trIdx) =>
- extendedWrapper(findHeadersRows().at(trIdx)).findByTestId('header-name-input');
- const findHeaderValueInput = (trIdx) =>
- extendedWrapper(findHeadersRows().at(trIdx)).findByTestId('header-value-input');
- const findHeaderActiveInput = (trIdx) =>
- extendedWrapper(findHeadersRows().at(trIdx)).findByTestId('header-active-input');
-
- const setHeaderNameInput = (trIdx, name) => findHeaderNameInput(trIdx).setValue(name);
- const setHeaderValueInput = (trIdx, value) => findHeaderValueInput(trIdx).setValue(value);
-
- const setHeadersRowData = async (trIdx, { name, value }) => {
- await setHeaderNameInput(trIdx, name);
- await setHeaderValueInput(trIdx, value);
- };
-
- const submitForm = async () => {
- findDestinationName().setValue('Name');
- findDestinationUrl().setValue('https://example.test');
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
- };
-
- const submitFormWithHeaders = async () => {
- findDestinationName().setValue('Name');
- findDestinationUrl().setValue('https://example.test');
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(0, { name: 'row header', value: 'row value' });
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(1, { name: 'row header 1', value: 'row value 1' });
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
- };
-
- afterEach(() => {
- createAlert.mockClear();
- });
-
- describe('when useConsolidatedAuditEventStreamDestApi is enabled', () => {
- const item = mockHttpTypeDestination[0];
-
- beforeEach(() => {
- createComponent({
- props: { item },
- provide: {
- glFeatures: { useConsolidatedAuditEventStreamDestApi: true },
- },
- });
- });
-
- it('renders the destination correctly', () => {
- expect(findWarningMessage().exists()).toBe(false);
- expect(findDestinationName().element.value).toBe('HTTP Destination 1');
- expect(findDestinationUrl().element.value).toBe('http://destination1.local');
- expect(findDestinationUrl().attributes('disabled')).toBeDefined();
- expect(findVerificationToken().props('value')).toBe('mockSecretToken');
- expect(findClipboardButton().props('text')).toBe('mockSecretToken');
- expect(findHeaderNameInput(0).element.value).toBe('key1');
- expect(findHeaderValueInput(0).element.value).toBe('test');
- expect(findHeaderActiveInput(0).element.value).toBe('true');
- expect(findFilters().props()).toStrictEqual({
- value: ['user_created'],
- });
- expect(findNamespaceFilters().props()).toMatchObject({
- value: mockNamespaceFilter('myGroup/project1'),
- });
- expect(findDeleteModal().props('item')).toBe(item);
- });
-
- describe('when there is an error on adding a destination header', () => {
- it('should call removeAuditEventsStreamingDestinationFromCache', async () => {
- createComponent({
- apolloHandlers: [
- [
- externalAuditEventDestinationCreate,
- jest.fn().mockResolvedValue(destinationCreateMutationPopulator()),
- ],
- [
- externalAuditEventDestinationHeaderCreate,
- jest.fn().mockResolvedValue(destinationHeaderCreateMutationPopulator(['error'])),
- ],
- [deleteExternalDestination, defaultDeleteSpy],
- ],
- provide: {
- glFeatures: { useConsolidatedAuditEventStreamDestApi: true },
- },
- });
-
- await submitFormWithHeaders();
-
- expect(removeAuditEventsStreamingDestinationFromCache).toHaveBeenCalled();
- });
- });
- });
-
- describe('Group StreamHttpDestinationEditor', () => {
- describe('when initialized', () => {
- describe('destinations URL', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('should render the destinations warning', () => {
- expect(findWarningMessage().props('title')).toBe(ADD_STREAM_EDITOR_I18N.WARNING_TITLE);
- expect(findWarningMessage().text()).toContain(ADD_STREAM_EDITOR_I18N.WARNING_CONTENT);
- });
-
- it('should render the destination URL input', () => {
- expect(findDestinationUrlFormGroup().exists()).toBe(true);
- expect(findDestinationUrl().props('disabled')).toBe(false);
- expect(findDestinationUrl().attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.DESTINATION_URL_PLACEHOLDER,
- );
- });
-
- it('should render the destination name input', () => {
- expect(findDestinationNameFormGroup().exists()).toBe(true);
- });
-
- it('should render the event type filter', () => {
- expect(findFilters().exists()).toBe(true);
- });
-
- it('should render the namespace filter', () => {
- expect(findNamespaceFilters().exists()).toBe(true);
- });
- });
-
- it('does not render verification token', () => {
- createComponent();
-
- expect(findVerificationTokenFormGroup().exists()).toBe(false);
- });
-
- describe('HTTP headers', () => {
- beforeEach(() => {
- createComponent({ mountFn: mountExtended });
- });
-
- it('should render no headers created message', () => {
- expect(findNoHeaderCreatedText().text()).toBe(
- ADD_STREAM_EDITOR_I18N.NO_HEADER_CREATED_TEXT,
- );
- });
-
- it('should render empty table row when adding new header', async () => {
- await findAddHeaderBtn().trigger('click');
-
- expect(findHeadersRows()).toHaveLength(1);
- expect(findNoHeaderCreatedText().exists()).toBe(false);
-
- expect(findHeadersHeaderCell(0).text()).toBe('');
- expect(findHeadersHeaderCell(1).text()).toBe('');
- expect(findHeadersHeaderCell(2).text()).toBe('');
- expect(findHeadersHeaderCell(3).text()).toBe('');
-
- expect(findHeaderNameInput(0).attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.HEADER_INPUT_PLACEHOLDER,
- );
- expect(findHeaderValueInput(0).attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.VALUE_INPUT_PLACEHOLDER,
- );
- expect(findHeaderCheckbox(0).find('input').attributes('value')).toBe('true');
- expect(findHeaderDeleteBtn(0).exists()).toBe(true);
- });
- });
-
- it('does not render delete button', () => {
- createComponent();
-
- expect(findDeleteBtn().exists()).toBe(false);
- });
- });
-
- describe('add destination event without headers', () => {
- it('should emit add event after destination added', async () => {
- createComponent();
-
- await submitForm();
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('added')).toBeDefined();
- });
-
- it('should not emit add destination event and reports error when server returns error', async () => {
- const errorMsg = 'Destination hosts limit exceeded';
- createComponent({
- apolloHandlers: [
- [
- externalAuditEventDestinationCreate,
- jest.fn().mockResolvedValue(destinationCreateMutationPopulator([errorMsg])),
- ],
- ],
- });
-
- await submitForm();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(AUDIT_STREAMS_NETWORK_ERRORS.CREATING_ERROR);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('added')).toBeUndefined();
- });
-
- it('should not emit add destination event and reports error when network error occurs', async () => {
- const sentryError = new Error('Network error');
- const sentryCaptureExceptionSpy = jest.spyOn(Sentry, 'captureException');
- createComponent({
- apolloHandlers: [
- [externalAuditEventDestinationCreate, jest.fn().mockRejectedValue(sentryError)],
- ],
- });
-
- await submitForm();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(AUDIT_STREAMS_NETWORK_ERRORS.CREATING_ERROR);
- expect(sentryCaptureExceptionSpy).toHaveBeenCalledWith(sentryError);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('added')).toBeUndefined();
- });
- });
-
- describe('add destination event with headers', () => {
- it('should emit add event after destination and headers are added', async () => {
- createComponent({
- mountFn: mountExtended,
- apolloHandlers: [
- [
- externalAuditEventDestinationCreate,
- jest.fn().mockResolvedValue(destinationCreateMutationPopulator()),
- ],
- [
- externalAuditEventDestinationHeaderCreate,
- jest.fn().mockResolvedValue(destinationHeaderCreateMutationPopulator()),
- ],
- ],
- });
-
- await submitFormWithHeaders();
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('added')).toBeDefined();
- });
-
- it('should not emit add destination event and reports error when server returns error while adding headers', async () => {
- const errorMsg = 'Destination hosts limit exceeded';
- createComponent({
- mountFn: mountExtended,
- apolloHandlers: [
- [
- externalAuditEventDestinationCreate,
- jest.fn().mockResolvedValue(destinationCreateMutationPopulator()),
- ],
- [
- externalAuditEventDestinationHeaderCreate,
- jest
- .fn()
- .mockResolvedValueOnce(destinationHeaderCreateMutationPopulator())
- .mockResolvedValue(destinationHeaderCreateMutationPopulator([errorMsg])),
- ],
- [deleteExternalDestination, defaultDeleteSpy],
- ],
- });
-
- await submitFormWithHeaders();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(errorMsg);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('added')).toBeUndefined();
- });
-
- it('should call removeLegacyAuditEventsStreamingDestination when server returns error while adding headers', async () => {
- createComponent({
- apolloHandlers: [
- [
- externalAuditEventDestinationCreate,
- jest.fn().mockResolvedValue(destinationCreateMutationPopulator()),
- ],
- [
- externalAuditEventDestinationHeaderCreate,
- jest.fn().mockResolvedValue(destinationHeaderCreateMutationPopulator(['error'])),
- ],
- [deleteExternalDestination, defaultDeleteSpy],
- ],
- });
-
- await submitFormWithHeaders();
-
- expect(removeLegacyAuditEventsStreamingDestination).toHaveBeenCalled();
- });
-
- it('should not emit add destination event and reports error when network error occurs while adding headers', async () => {
- const sentryError = new Error('Network error');
- const sentryCaptureExceptionSpy = jest.spyOn(Sentry, 'captureException');
- createComponent({
- mountFn: mountExtended,
- apolloHandlers: [
- [
- externalAuditEventDestinationCreate,
- jest.fn().mockResolvedValue(destinationCreateMutationPopulator()),
- ],
- [
- externalAuditEventDestinationHeaderCreate,
- jest
- .fn()
- .mockResolvedValueOnce(destinationHeaderCreateMutationPopulator())
- .mockRejectedValue(sentryError),
- ],
- [deleteExternalDestination, defaultDeleteSpy],
- ],
- });
-
- await submitFormWithHeaders();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(AUDIT_STREAMS_NETWORK_ERRORS.CREATING_ERROR);
- expect(sentryCaptureExceptionSpy).toHaveBeenCalledWith(sentryError);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('added')).toBeUndefined();
- });
- });
-
- describe('cancel event', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('should emit cancel event correctly', () => {
- findCancelStreamBtn().vm.$emit('click');
-
- expect(wrapper.emitted('cancel')).toBeDefined();
- });
- });
-
- describe('HTTP headers table', () => {
- beforeEach(() => {
- createComponent({ mountFn: mountExtended });
- });
-
- it('should add a new blank row if the add row button is clicked', async () => {
- expect(findHeadersRows()).toHaveLength(0);
-
- await findAddHeaderBtn().trigger('click');
-
- expect(findHeadersRows()).toHaveLength(1);
- });
-
- it.each`
- name | value | disabled
- ${''} | ${''} | ${true}
- ${'abc'} | ${''} | ${true}
- ${''} | ${'abc'} | ${true}
- ${'abc'} | ${'abc'} | ${false}
- `(
- 'should enable the add button only when both the name and value are filled',
- async ({ name, value, disabled }) => {
- findDestinationName().setValue('Name');
- findDestinationUrl().setValue('https://example.test');
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(0, { name, value });
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(disabled);
- },
- );
-
- it('disables add button when there are headers with the same name', async () => {
- findDestinationName().setValue('Name');
- findDestinationUrl().setValue('https://example.test');
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(0, { name: 'a', value: 'b' });
-
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(1, { name: 'a', value: 'c' });
- expect(findSubmitStreamBtn().props('disabled')).toBe(true);
- });
-
- it('should delete a row when the delete button is clicked', async () => {
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(0, { name: 'row header', value: 'row value' });
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(1, { name: 'row header 2', value: 'row value 2' });
- await findAddHeaderBtn().trigger('click');
-
- expect(findHeadersRows()).toHaveLength(3);
-
- await findHeaderDeleteBtn(1).trigger('click');
-
- expect(findHeadersRows()).toHaveLength(2);
- expect(findHeaderNameInput(0).element.value).toBe('row header');
- expect(findHeaderValueInput(0).element.value).toBe('row value');
- expect(findHeaderNameInput(1).element.value).toBe('');
- expect(findHeaderValueInput(1).element.value).toBe('');
- });
-
- it('enables add button when first header with the same name is deleted', async () => {
- findDestinationName().setValue('Name');
- findDestinationUrl().setValue('https://example.test');
-
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(0, { name: 'a', value: 'b' });
-
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(1, { name: 'a', value: 'c' });
-
- await findHeaderDeleteBtn(0).trigger('click');
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(false);
- });
-
- it('keeps add button disabled when invalid header with the same name is deleted', async () => {
- findDestinationName().setValue('Name');
- findDestinationUrl().setValue('https://example.test');
-
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(0, { name: 'a', value: 'b' });
-
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(1, { name: 'a', value: 'c' });
-
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(1, { name: 'a', value: 'c' });
-
- await findHeaderDeleteBtn(1).trigger('click');
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(true);
- });
-
- it('should show the maximum number of rows message only when the maximum is reached', async () => {
- await findAddHeaderBtn().trigger('click');
- await findAddHeaderBtn().trigger('click');
-
- expect(findMaximumHeadersText().exists()).toBe(false);
-
- await findAddHeaderBtn().trigger('click');
-
- expect(findHeadersRows()).toHaveLength(maxHeaders);
- expect(findAddHeaderBtn().exists()).toBe(false);
- expect(findMaximumHeadersText().text()).toMatchInterpolatedText(
- sprintf(ADD_STREAM_EDITOR_I18N.MAXIMUM_HEADERS_TEXT, { number: maxHeaders }),
- );
- });
- });
-
- describe('when editing an existing destination', () => {
- const item = {
- ...mockExternalDestinations[0],
- headers: { nodes: [mockExternalDestinationHeader(), mockExternalDestinationHeader()] },
- };
-
- describe('renders', () => {
- beforeEach(() => {
- createComponent({ mountFn: mountExtended, props: { item } });
- });
-
- it('renders the delete modal', () => {
- expect(findDeleteModal().props('item')).toBe(item);
- });
-
- it('should not render the destinations warning', () => {
- expect(findWarningMessage().exists()).toBe(false);
- });
-
- it('disables the destination URL field', () => {
- expect(findDestinationUrl().element.value).toBe(
- mockExternalDestinations[0].destinationUrl,
- );
- expect(findDestinationUrl().attributes('disabled')).toBeDefined();
- });
-
- it('renders verification token and clipboard button', () => {
- expect(findVerificationTokenFormGroup().classes('gl-max-w-34')).toBe(true);
- expect(findVerificationToken().attributes('readonly')).toBeDefined();
- expect(findVerificationToken().props('value')).toBe(item.verificationToken);
- expect(findClipboardButton().props('text')).toBe(item.verificationToken);
- expect(findClipboardButton().props('title')).toBe('Copy to clipboard');
- });
-
- it('changes the save button text', () => {
- expect(findSubmitStreamBtn().attributes('name')).toBe(
- ADD_STREAM_EDITOR_I18N.SAVE_BUTTON_NAME,
- );
- expect(findSubmitStreamBtn().text()).toBe(ADD_STREAM_EDITOR_I18N.SAVE_BUTTON_TEXT);
- });
-
- it('disables the save button text at first', () => {
- expect(findSubmitStreamBtn().props('disabled')).toBe(true);
- });
-
- it('renders the delete button', () => {
- expect(findDeleteBtn().attributes('name')).toBe(
- ADD_STREAM_EDITOR_I18N.DELETE_BUTTON_TEXT,
- );
- expect(findDeleteBtn().classes('gl-ml-auto')).toBe(true);
- expect(findDeleteBtn().props('variant')).toBe('danger');
- expect(findDeleteBtn().text()).toBe(ADD_STREAM_EDITOR_I18N.DELETE_BUTTON_TEXT);
- });
- });
-
- describe('update destinations headers', () => {
- const updatedHeader = { ...item.headers.nodes[0], newValue: 'CHANGED_VALUE' };
- const deletedHeader = item.headers.nodes[1];
- const addedHeader = mockExternalDestinationHeader();
-
- const setupUpdatedHeaders = async (updated, added) => {
- findDestinationName().setValue('Name');
- findDestinationUrl().setValue('https://example.test');
- await setHeadersRowData(0, { name: updated.key, value: updated.newValue });
- await findHeaderDeleteBtn(1).trigger('click');
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(1, { name: added.key, value: added.value });
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
-
- return waitForPromises();
- };
-
- it('emits the updated event when the headers are added, updated, and deleted', async () => {
- const destinationUpdateSpy = jest
- .fn()
- .mockResolvedValue(destinationUpdateMutationPopulator());
- const headerCreateSpy = jest
- .fn()
- .mockResolvedValue(destinationHeaderCreateMutationPopulator());
- const headerUpdateSpy = jest
- .fn()
- .mockResolvedValue(destinationHeaderUpdateMutationPopulator());
- const headerDeleteSpy = jest
- .fn()
- .mockResolvedValue(destinationHeaderDeleteMutationPopulator());
-
- createComponent({
- mountFn: mountExtended,
- props: { item },
- apolloHandlers: [
- [externalAuditEventDestinationUpdate, destinationUpdateSpy],
- [externalAuditEventDestinationHeaderCreate, headerCreateSpy],
- [externalAuditEventDestinationHeaderUpdate, headerUpdateSpy],
- [externalAuditEventDestinationHeaderDelete, headerDeleteSpy],
- ],
- });
-
- await setupUpdatedHeaders(updatedHeader, addedHeader);
-
- expect(destinationUpdateSpy).toHaveBeenCalledTimes(1);
-
- expect(headerDeleteSpy).toHaveBeenCalledTimes(1);
- expect(headerDeleteSpy).toHaveBeenCalledWith({
- headerId: deletedHeader.id,
- });
- expect(headerUpdateSpy).toHaveBeenCalledTimes(1);
- expect(headerUpdateSpy).toHaveBeenCalledWith({
- headerId: updatedHeader.id,
- key: updatedHeader.key,
- value: updatedHeader.newValue,
- active: false,
- });
- expect(headerCreateSpy).toHaveBeenCalledTimes(1);
- expect(headerCreateSpy).toHaveBeenCalledWith({
- destinationId: item.id,
- key: addedHeader.key,
- value: addedHeader.value,
- active: true,
- });
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('updated')).toBeDefined();
- });
-
- it('handles adding & removal header with the same name', async () => {
- const destinationUpdateSpy = jest
- .fn()
- .mockResolvedValue(destinationUpdateMutationPopulator());
- const headerCreateSpy = jest
- .fn()
- .mockResolvedValue(destinationHeaderCreateMutationPopulator());
- const headerDeleteSpy = jest
- .fn()
- .mockResolvedValue(destinationHeaderDeleteMutationPopulator());
-
- createComponent({
- mountFn: mountExtended,
- props: { item },
- apolloHandlers: [
- [externalAuditEventDestinationUpdate, destinationUpdateSpy],
- [externalAuditEventDestinationHeaderCreate, headerCreateSpy],
- [externalAuditEventDestinationHeaderDelete, headerDeleteSpy],
- ],
- });
- await waitForPromises();
-
- findDestinationName().setValue('Name');
- findDestinationUrl().setValue('https://example.test');
- await findHeaderDeleteBtn(1).trigger('click');
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(1, { name: item.headers.nodes[0].key, value: 'NEW' });
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
-
- await waitForPromises();
-
- expect(headerDeleteSpy).toHaveBeenCalledTimes(1);
- expect(headerDeleteSpy).toHaveBeenCalledWith({
- headerId: deletedHeader.id,
- });
- expect(headerCreateSpy).toHaveBeenCalledTimes(1);
- expect(headerCreateSpy).toHaveBeenCalledWith({
- destinationId: item.id,
- key: item.headers.nodes[0].key,
- value: 'NEW',
- active: true,
- });
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('updated')).toBeDefined();
- });
-
- it('should not emit updated event and reports error when server returns error while saving', async () => {
- const errorMsg = 'Destination hosts limit exceeded';
-
- createComponent({
- mountFn: mountExtended,
- props: { item },
- apolloHandlers: [
- [
- externalAuditEventDestinationUpdate,
- jest.fn().mockResolvedValue(destinationUpdateMutationPopulator()),
- ],
- [
- externalAuditEventDestinationHeaderCreate,
- jest.fn().mockResolvedValue(destinationHeaderCreateMutationPopulator([errorMsg])),
- ],
- [
- externalAuditEventDestinationHeaderUpdate,
- jest.fn().mockResolvedValue(destinationHeaderUpdateMutationPopulator()),
- ],
- [
- externalAuditEventDestinationHeaderDelete,
- jest.fn().mockResolvedValue(destinationHeaderDeleteMutationPopulator()),
- ],
- ],
- });
-
- await setupUpdatedHeaders(updatedHeader, addedHeader);
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(errorMsg);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('updated')).toBeUndefined();
- });
-
- it('should not emit updated event and reports error when network error occurs while saving', async () => {
- const sentryError = new Error('Network error');
- const sentryCaptureExceptionSpy = jest.spyOn(Sentry, 'captureException');
-
- createComponent({
- mountFn: mountExtended,
- props: { item },
- apolloHandlers: [
- [
- externalAuditEventDestinationUpdate,
- jest.fn().mockResolvedValue(destinationUpdateMutationPopulator()),
- ],
- [externalAuditEventDestinationHeaderUpdate, jest.fn().mockRejectedValue(sentryError)],
- [
- externalAuditEventDestinationHeaderDelete,
- jest.fn().mockResolvedValue(destinationHeaderDeleteMutationPopulator()),
- ],
- ],
- });
-
- await setupUpdatedHeaders(updatedHeader, addedHeader);
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(AUDIT_STREAMS_NETWORK_ERRORS.UPDATING_ERROR);
- expect(sentryCaptureExceptionSpy).toHaveBeenCalledWith(sentryError);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('updated')).toBeUndefined();
- });
- });
-
- describe('deleting', () => {
- beforeEach(() => {
- createComponent({ mountFn: mountExtended, props: { item } });
- });
-
- it('should emit deleted on success operation', async () => {
- const deleteButton = findDeleteBtn();
- await deleteButton.trigger('click');
- await findDeleteModal().vm.$emit('deleting');
-
- expect(deleteButton.props('loading')).toBe(true);
-
- await findDeleteModal().vm.$emit('delete');
-
- expect(deleteButton.props('loading')).toBe(false);
- expect(wrapper.emitted('deleted')).toEqual([[item.id]]);
- });
-
- it('shows the alert for the error', () => {
- const errorMsg = 'An error occurred';
- findDeleteModal().vm.$emit('error', errorMsg);
-
- expect(createAlert).toHaveBeenCalledWith({
- message: AUDIT_STREAMS_NETWORK_ERRORS.DELETING_ERROR,
- captureError: true,
- error: errorMsg,
- });
- });
- });
- });
-
- describe('destination event type filters', () => {
- describe('renders', () => {
- beforeEach(() => {
- createComponent({ mountFn: mountExtended, props: { item: mockExternalDestinations[1] } });
- });
-
- it('displays the correct text', () => {
- expect(findFilteringHeader().text()).toBe(ADD_STREAM_EDITOR_I18N.HEADER_FILTERING);
- expect(findEventTypeFilteringHeader().text()).toBe(
- ADD_STREAM_EDITOR_I18N.FILTER_BY_AUDIT_EVENT_TYPE,
- );
- });
-
- it('passes selected audit event types to StreamEventTypeFilters', () => {
- expect(findFilters().props()).toStrictEqual({
- value: mockExternalDestinations[1].eventTypeFilters,
- });
- });
- });
-
- describe('on change filters', () => {
- it('removes the deselected filters from a destination', async () => {
- const destinationUpdateSpy = jest
- .fn()
- .mockResolvedValue(destinationUpdateMutationPopulator());
-
- const filterRemoveSpy = jest
- .fn()
- .mockResolvedValue(destinationFilterRemoveMutationPopulator());
-
- createComponent({
- mountFn: mountExtended,
- props: { item: mockExternalDestinations[1] },
- apolloHandlers: [
- [externalAuditEventDestinationUpdate, destinationUpdateSpy],
- [deleteExternalDestinationFilters, filterRemoveSpy],
- ],
- });
-
- await findFilters().vm.$emit('input', mockRemoveFilterSelect);
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(false);
-
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(filterRemoveSpy).toHaveBeenCalledWith({
- destinationId: mockExternalDestinations[1].id,
- eventTypeFilters: mockRemoveFilterRemaining,
- });
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('updated')).toBeDefined();
- });
-
- it('adds the selected filters for a destination', async () => {
- const destinationUpdateSpy = jest
- .fn()
- .mockResolvedValue(destinationUpdateMutationPopulator());
-
- const filterAddSpy = jest
- .fn()
- .mockResolvedValue(destinationFilterUpdateMutationPopulator());
-
- createComponent({
- mountFn: mountExtended,
- props: { item: mockExternalDestinations[1] },
- apolloHandlers: [
- [externalAuditEventDestinationUpdate, destinationUpdateSpy],
- [addExternalDestinationFilters, filterAddSpy],
- ],
- });
-
- await findFilters().vm.$emit('input', mockAddFilterSelect);
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(false);
-
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(filterAddSpy).toHaveBeenCalledWith({
- destinationId: mockExternalDestinations[1].id,
- eventTypeFilters: mockAddFilterRemaining,
- });
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('updated')).toBeDefined();
- });
-
- it('should not emit updated event and reports error when network error occurs while saving', async () => {
- const sentryError = new Error('Network error');
- const sentryCaptureExceptionSpy = jest.spyOn(Sentry, 'captureException');
- const destinationUpdateSpy = jest
- .fn()
- .mockResolvedValue(destinationUpdateMutationPopulator());
- const filterRemoveSpy = jest.fn().mockRejectedValue(sentryError);
-
- createComponent({
- mountFn: mountExtended,
- props: { item: mockExternalDestinations[1] },
- apolloHandlers: [
- [externalAuditEventDestinationUpdate, destinationUpdateSpy],
- [deleteExternalDestinationFilters, filterRemoveSpy],
- ],
- });
-
- findFilters().vm.$emit('input', mockRemoveFilterSelect);
-
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(AUDIT_STREAMS_NETWORK_ERRORS.UPDATING_ERROR);
- expect(sentryCaptureExceptionSpy).toHaveBeenCalledWith(sentryError);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('updated')).toBeUndefined();
- });
- });
- });
-
- describe('destination namespace filters', () => {
- describe('renders', () => {
- beforeEach(() => {
- createComponent({ mountFn: mountExtended, props: { item: mockExternalDestinations[1] } });
- });
-
- it('displays the correct text', () => {
- expect(findNamespaceFilteringHeader().text()).toBe(
- ADD_STREAM_EDITOR_I18N.FILTER_BY_NAMESPACE,
- );
- });
-
- it('passes selected namespace to StreamEventTypeFilters', () => {
- expect(findNamespaceFilters().props()).toMatchObject({
- value: mockNamespaceFilter(
- mockExternalDestinations[1].namespaceFilter.namespace.fullPath,
- ),
- });
- });
- });
-
- describe('on change filters', () => {
- it('removes the current filter from a destination', async () => {
- const destinationUpdateSpy = jest
- .fn()
- .mockResolvedValue(destinationUpdateMutationPopulator());
-
- const namespaceFilterRemoveSpy = jest
- .fn()
- .mockResolvedValue(destinationNamespaceFilterRemoveMutationPopulator());
-
- createComponent({
- mountFn: mountExtended,
- props: { item: mockExternalDestinations[1] },
- apolloHandlers: [
- [externalAuditEventDestinationUpdate, destinationUpdateSpy],
- [deleteExternalDestinationNamespaceFilters, namespaceFilterRemoveSpy],
- ],
- });
-
- await findNamespaceFilters().vm.$emit('input', mockRemoveNamespaceFilters);
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(false);
-
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(namespaceFilterRemoveSpy).toHaveBeenCalledWith({
- namespaceFilterId: mockExternalDestinations[1].namespaceFilter.id,
- });
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('updated')).toBeDefined();
- });
-
- it('adds the selected filter for a destination', async () => {
- const destinationUpdateSpy = jest
- .fn()
- .mockResolvedValue(destinationUpdateMutationPopulator());
-
- const namespaceFilterAddSpy = jest
- .fn()
- .mockResolvedValue(destinationNamespaceFilterAddMutationPopulator());
-
- createComponent({
- mountFn: mountExtended,
- props: { item: mockExternalDestinations[0] },
- apolloHandlers: [
- [externalAuditEventDestinationUpdate, destinationUpdateSpy],
- [addExternalDestinationNamespaceFilters, namespaceFilterAddSpy],
- ],
- });
-
- await findNamespaceFilters().vm.$emit('input', mockAddNamespaceFilters);
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(false);
-
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(namespaceFilterAddSpy).toHaveBeenCalledWith({
- destinationId: mockExternalDestinations[0].id,
- projectPath: mockAddNamespaceFilters.namespace,
- });
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('updated')).toBeDefined();
- });
-
- it('should not emit updated event and reports error when network error occurs while saving', async () => {
- const sentryError = new Error('Network error');
- const sentryCaptureExceptionSpy = jest.spyOn(Sentry, 'captureException');
- const destinationUpdateSpy = jest
- .fn()
- .mockResolvedValue(destinationUpdateMutationPopulator());
- const filterRemoveSpy = jest.fn().mockRejectedValue(sentryError);
-
- createComponent({
- mountFn: mountExtended,
- props: { item: mockExternalDestinations[1] },
- apolloHandlers: [
- [externalAuditEventDestinationUpdate, destinationUpdateSpy],
- [deleteExternalDestinationNamespaceFilters, filterRemoveSpy],
- ],
- });
-
- findNamespaceFilters().vm.$emit('input', mockRemoveNamespaceFilters);
-
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(AUDIT_STREAMS_NETWORK_ERRORS.UPDATING_ERROR);
- expect(sentryCaptureExceptionSpy).toHaveBeenCalledWith(sentryError);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('updated')).toBeUndefined();
- });
- });
- });
- });
-
- describe('Instance StreamHttpDestinationEditor', () => {
- beforeEach(() => {
- groupPathProvide = instanceGroupPath;
- });
-
- describe('when initialized', () => {
- describe('destinations URL', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('should render the destinations warning', () => {
- expect(findWarningMessage().props('title')).toBe(ADD_STREAM_EDITOR_I18N.WARNING_TITLE);
- expect(findWarningMessage().text()).toContain(ADD_STREAM_EDITOR_I18N.WARNING_CONTENT);
- });
-
- it('should render the destination URL input', () => {
- expect(findDestinationUrlFormGroup().exists()).toBe(true);
- expect(findDestinationUrl().props('disabled')).toBe(false);
- expect(findDestinationUrl().attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.DESTINATION_URL_PLACEHOLDER,
- );
- });
-
- it('does not render verification token', () => {
- expect(findVerificationTokenFormGroup().exists()).toBe(false);
- });
-
- it('does not render delete button', () => {
- createComponent();
-
- expect(findDeleteBtn().exists()).toBe(false);
- });
- });
-
- describe('HTTP headers', () => {
- beforeEach(() => {
- createComponent({ mountFn: mountExtended });
- });
-
- it('should render no headers created message', () => {
- expect(findNoHeaderCreatedText().text()).toBe(
- ADD_STREAM_EDITOR_I18N.NO_HEADER_CREATED_TEXT,
- );
- });
-
- it('should render empty table row when adding new header', async () => {
- await findAddHeaderBtn().trigger('click');
-
- expect(findHeadersRows()).toHaveLength(1);
- expect(findNoHeaderCreatedText().exists()).toBe(false);
-
- expect(findHeadersHeaderCell(0).text()).toBe('');
- expect(findHeadersHeaderCell(1).text()).toBe('');
- expect(findHeadersHeaderCell(2).text()).toBe('');
- expect(findHeadersHeaderCell(3).text()).toBe('');
-
- expect(findHeaderNameInput(0).attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.HEADER_INPUT_PLACEHOLDER,
- );
- expect(findHeaderValueInput(0).attributes('placeholder')).toBe(
- ADD_STREAM_EDITOR_I18N.VALUE_INPUT_PLACEHOLDER,
- );
- expect(findHeaderCheckbox(0).find('input').attributes('value')).toBe('true');
- expect(findHeaderDeleteBtn(0).exists()).toBe(true);
- });
- });
- });
-
- describe('add destination event', () => {
- it('should emit add event after destination added', async () => {
- createComponent({
- apolloHandlers: [
- [
- instanceExternalAuditEventDestinationCreate,
- jest.fn().mockResolvedValue(destinationInstanceCreateMutationPopulator()),
- ],
- ],
- });
-
- await submitForm();
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('added')).toBeDefined();
- });
-
- it('should not emit add destination event and reports error when server returns error', async () => {
- const errorMsg = 'Destination hosts limit exceeded';
- createComponent({
- apolloHandlers: [
- [
- instanceExternalAuditEventDestinationCreate,
- jest.fn().mockResolvedValue(destinationInstanceCreateMutationPopulator([errorMsg])),
- ],
- ],
- });
-
- await submitForm();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(errorMsg);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('added')).toBeUndefined();
- });
-
- it('should not emit add destination event and reports error when network error occurs', async () => {
- const sentryError = new Error('Network error');
- const sentryCaptureExceptionSpy = jest.spyOn(Sentry, 'captureException');
- createComponent({
- apolloHandlers: [
- [instanceExternalAuditEventDestinationCreate, jest.fn().mockRejectedValue(sentryError)],
- ],
- });
-
- await submitForm();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(AUDIT_STREAMS_NETWORK_ERRORS.CREATING_ERROR);
- expect(sentryCaptureExceptionSpy).toHaveBeenCalledWith(sentryError);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('added')).toBeUndefined();
- });
- });
-
- describe('add destination event with headers', () => {
- it('should emit add event after destination and headers are added', async () => {
- createComponent({
- mountFn: mountExtended,
- apolloHandlers: [
- [
- instanceExternalAuditEventDestinationCreate,
- jest.fn().mockResolvedValue(destinationInstanceCreateMutationPopulator()),
- ],
- [
- externalInstanceAuditEventDestinationHeaderCreate,
- jest.fn().mockResolvedValue(destinationInstanceHeaderCreateMutationPopulator()),
- ],
- ],
- });
-
- await submitFormWithHeaders();
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('added')).toBeDefined();
- });
-
- it('should not emit add destination event and reports error when server returns error while adding headers', async () => {
- const errorMsg = 'Destination hosts limit exceeded';
- createComponent({
- mountFn: mountExtended,
- apolloHandlers: [
- [
- instanceExternalAuditEventDestinationCreate,
- jest.fn().mockResolvedValue(destinationInstanceCreateMutationPopulator()),
- ],
- [
- externalInstanceAuditEventDestinationHeaderCreate,
- jest
- .fn()
- .mockResolvedValueOnce(destinationInstanceHeaderCreateMutationPopulator())
- .mockResolvedValue(destinationInstanceHeaderCreateMutationPopulator([errorMsg])),
- ],
- [
- deleteInstanceExternalDestination,
- jest.fn().mockResolvedValue(destinationInstanceDeleteMutationPopulator()),
- ],
- ],
- });
-
- await submitFormWithHeaders();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(errorMsg);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('added')).toBeUndefined();
- });
-
- it('should not emit add destination event and reports error when network error occurs while adding headers', async () => {
- const sentryError = new Error('Network error');
- const sentryCaptureExceptionSpy = jest.spyOn(Sentry, 'captureException');
- createComponent({
- mountFn: mountExtended,
- apolloHandlers: [
- [
- instanceExternalAuditEventDestinationCreate,
- jest.fn().mockResolvedValue(destinationInstanceCreateMutationPopulator()),
- ],
- [
- externalInstanceAuditEventDestinationHeaderCreate,
- jest
- .fn()
- .mockResolvedValueOnce(destinationInstanceHeaderCreateMutationPopulator())
- .mockRejectedValue(sentryError),
- ],
- [
- deleteInstanceExternalDestination,
- jest.fn().mockResolvedValue(destinationInstanceDeleteMutationPopulator()),
- ],
- ],
- });
-
- await submitFormWithHeaders();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(AUDIT_STREAMS_NETWORK_ERRORS.CREATING_ERROR);
- expect(sentryCaptureExceptionSpy).toHaveBeenCalledWith(sentryError);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('added')).toBeUndefined();
- });
- });
-
- describe('cancel event', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('should emit cancel event correctly', () => {
- findCancelStreamBtn().vm.$emit('click');
-
- expect(wrapper.emitted('cancel')).toBeDefined();
- });
- });
-
- describe('HTTP headers table', () => {
- beforeEach(() => {
- createComponent({ mountFn: mountExtended });
- });
-
- it('should add a new blank row if the add row button is clicked', async () => {
- expect(findHeadersRows()).toHaveLength(0);
-
- await findAddHeaderBtn().trigger('click');
-
- expect(findHeadersRows()).toHaveLength(1);
- });
-
- it.each`
- name | value | disabled
- ${''} | ${''} | ${true}
- ${'abc'} | ${''} | ${true}
- ${''} | ${'abc'} | ${true}
- ${'abc'} | ${'abc'} | ${false}
- `(
- 'should enable the add button only when both the name and value are filled',
- async ({ name, value, disabled }) => {
- findDestinationName().setValue('Name');
- findDestinationUrl().setValue('https://example.test');
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(0, { name, value });
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(disabled);
- },
- );
-
- it('disables add button when there are headers with the same name', async () => {
- findDestinationName().setValue('Name');
- findDestinationUrl().setValue('https://example.test');
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(0, { name: 'a', value: 'b' });
-
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(1, { name: 'a', value: 'c' });
- expect(findSubmitStreamBtn().props('disabled')).toBe(true);
- });
-
- it('should delete a row when the delete button is clicked', async () => {
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(0, { name: 'row header', value: 'row value' });
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(1, { name: 'row header 2', value: 'row value 2' });
- await findAddHeaderBtn().trigger('click');
-
- expect(findHeadersRows()).toHaveLength(3);
-
- await findHeaderDeleteBtn(1).trigger('click');
-
- expect(findHeadersRows()).toHaveLength(2);
- expect(findHeaderNameInput(0).element.value).toBe('row header');
- expect(findHeaderValueInput(0).element.value).toBe('row value');
- expect(findHeaderNameInput(1).element.value).toBe('');
- expect(findHeaderValueInput(1).element.value).toBe('');
- });
-
- it('should show the maximum number of rows message only when the maximum is reached', async () => {
- await findAddHeaderBtn().trigger('click');
- await findAddHeaderBtn().trigger('click');
-
- expect(findMaximumHeadersText().exists()).toBe(false);
-
- await findAddHeaderBtn().trigger('click');
-
- expect(findHeadersRows()).toHaveLength(maxHeaders);
- expect(findAddHeaderBtn().exists()).toBe(false);
- expect(findMaximumHeadersText().text()).toMatchInterpolatedText(
- sprintf(ADD_STREAM_EDITOR_I18N.MAXIMUM_HEADERS_TEXT, { number: maxHeaders }),
- );
- });
- });
-
- describe('when editing an existing destination', () => {
- const item = {
- ...mockInstanceExternalDestinations[0],
- headers: {
- nodes: [mockInstanceExternalDestinationHeader(), mockInstanceExternalDestinationHeader()],
- },
- };
-
- describe('renders', () => {
- beforeEach(() => {
- createComponent({ mountFn: mountExtended, props: { item } });
- });
-
- it('renders the delete modal', () => {
- expect(findDeleteModal().exists()).toBe(true);
- expect(findDeleteModal().props('item')).toBe(item);
- });
-
- it('should not render the destinations warning', () => {
- expect(findWarningMessage().exists()).toBe(false);
- });
-
- it('disables the destination URL field', () => {
- expect(findDestinationUrl().element.value).toBe(
- mockInstanceExternalDestinations[0].destinationUrl,
- );
- expect(findDestinationUrl().attributes('disabled')).toBeDefined();
- });
-
- it('renders verification token and clipboard button', () => {
- expect(findVerificationTokenFormGroup().classes('gl-max-w-34')).toBe(true);
- expect(findVerificationToken().attributes('readonly')).toBeDefined();
- expect(findVerificationToken().props('value')).toBe(item.verificationToken);
- expect(findClipboardButton().props('text')).toBe(item.verificationToken);
- expect(findClipboardButton().props('title')).toBe('Copy to clipboard');
- });
-
- it('changes the save button text', () => {
- expect(findSubmitStreamBtn().attributes('name')).toBe(
- ADD_STREAM_EDITOR_I18N.SAVE_BUTTON_NAME,
- );
- expect(findSubmitStreamBtn().text()).toBe(ADD_STREAM_EDITOR_I18N.SAVE_BUTTON_TEXT);
- });
-
- it('disables the save button text at first', () => {
- expect(findSubmitStreamBtn().props('disabled')).toBe(true);
- });
-
- it('renders the delete button', () => {
- expect(findDeleteBtn().attributes('name')).toBe(
- ADD_STREAM_EDITOR_I18N.DELETE_BUTTON_TEXT,
- );
- expect(findDeleteBtn().classes('gl-ml-auto')).toBe(true);
- expect(findDeleteBtn().props('variant')).toBe('danger');
- expect(findDeleteBtn().text()).toBe(ADD_STREAM_EDITOR_I18N.DELETE_BUTTON_TEXT);
- });
- });
-
- describe('update destinations headers', () => {
- const updatedHeader = { ...item.headers.nodes[0], newValue: 'CHANGED_VALUE' };
- const deletedHeader = item.headers.nodes[1];
- const addedHeader = mockInstanceExternalDestinationHeader();
-
- const setupUpdatedHeaders = async (updated, added) => {
- findDestinationName().setValue('Name');
- findDestinationUrl().setValue('https://example.test');
- await setHeadersRowData(0, { name: updated.key, value: updated.newValue });
- await findHeaderDeleteBtn(1).trigger('click');
- await findAddHeaderBtn().trigger('click');
- await setHeadersRowData(1, { name: added.key, value: added.value });
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
-
- return waitForPromises();
- };
-
- it('emits the updated event when the headers are added, updated, and deleted', async () => {
- const destinationUpdateSpy = jest
- .fn()
- .mockResolvedValue(destinationInstanceUpdateMutationPopulator());
- const headerCreateSpy = jest
- .fn()
- .mockResolvedValue(destinationInstanceHeaderCreateMutationPopulator());
- const headerUpdateSpy = jest
- .fn()
- .mockResolvedValue(destinationInstanceHeaderUpdateMutationPopulator());
- const headerDeleteSpy = jest
- .fn()
- .mockResolvedValue(destinationInstanceHeaderDeleteMutationPopulator());
-
- createComponent({
- mountFn: mountExtended,
- props: { item },
- apolloHandlers: [
- [instanceExternalAuditEventDestinationUpdate, destinationUpdateSpy],
- [externalInstanceAuditEventDestinationHeaderCreate, headerCreateSpy],
- [externalInstanceAuditEventDestinationHeaderUpdate, headerUpdateSpy],
- [externalInstanceAuditEventDestinationHeaderDelete, headerDeleteSpy],
- ],
- });
-
- await setupUpdatedHeaders(updatedHeader, addedHeader);
-
- expect(destinationUpdateSpy).toHaveBeenCalledTimes(1);
-
- expect(headerDeleteSpy).toHaveBeenCalledTimes(1);
- expect(headerDeleteSpy).toHaveBeenCalledWith({
- headerId: deletedHeader.id,
- });
- expect(headerUpdateSpy).toHaveBeenCalledTimes(1);
- expect(headerUpdateSpy).toHaveBeenCalledWith({
- headerId: updatedHeader.id,
- key: updatedHeader.key,
- value: updatedHeader.newValue,
- active: false,
- });
- expect(headerCreateSpy).toHaveBeenCalledTimes(1);
- expect(headerCreateSpy).toHaveBeenCalledWith({
- destinationId: item.id,
- key: addedHeader.key,
- value: addedHeader.value,
- active: true,
- });
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('updated')).toBeDefined();
- });
-
- it('should not emit updated event and reports error when server returns error while saving', async () => {
- const errorMsg =
- 'An error occurred when updating external audit event stream destination. Please try it again.';
-
- createComponent({
- mountFn: mountExtended,
- props: { item },
- apolloHandlers: [
- [
- instanceExternalAuditEventDestinationUpdate,
- jest.fn().mockResolvedValue(destinationInstanceUpdateMutationPopulator()),
- ],
- [
- externalInstanceAuditEventDestinationHeaderCreate,
- jest
- .fn()
- .mockResolvedValue(destinationInstanceHeaderCreateMutationPopulator([errorMsg])),
- ],
- [
- externalInstanceAuditEventDestinationHeaderUpdate,
- jest.fn().mockResolvedValue(destinationInstanceHeaderUpdateMutationPopulator()),
- ],
- [
- externalInstanceAuditEventDestinationHeaderDelete,
- jest.fn().mockResolvedValue(destinationInstanceHeaderDeleteMutationPopulator()),
- ],
- ],
- });
-
- await setupUpdatedHeaders(updatedHeader, addedHeader);
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(errorMsg);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('updated')).toBeUndefined();
- });
-
- it('should not emit updated event and reports error when network error occurs while saving', async () => {
- const sentryError = new Error('Network error');
- const sentryCaptureExceptionSpy = jest.spyOn(Sentry, 'captureException');
-
- createComponent({
- mountFn: mountExtended,
- props: { item },
- apolloHandlers: [
- [
- instanceExternalAuditEventDestinationUpdate,
- jest.fn().mockResolvedValue(destinationInstanceUpdateMutationPopulator()),
- ],
- [
- externalInstanceAuditEventDestinationHeaderUpdate,
- jest.fn().mockRejectedValue(sentryError),
- ],
- [
- externalInstanceAuditEventDestinationHeaderDelete,
- jest.fn().mockResolvedValue(destinationInstanceHeaderDeleteMutationPopulator()),
- ],
- ],
- });
-
- await setupUpdatedHeaders(updatedHeader, addedHeader);
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(AUDIT_STREAMS_NETWORK_ERRORS.UPDATING_ERROR);
- expect(sentryCaptureExceptionSpy).toHaveBeenCalledWith(sentryError);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('updated')).toBeUndefined();
- });
- });
-
- describe('deleting', () => {
- beforeEach(() => {
- createComponent({ mountFn: mountExtended, props: { item } });
- });
-
- it('should emit deleted on success operation', async () => {
- const deleteButton = findDeleteBtn();
- await deleteButton.trigger('click');
- await findDeleteModal().vm.$emit('deleting');
-
- expect(deleteButton.props('loading')).toBe(true);
-
- await findDeleteModal().vm.$emit('delete');
-
- expect(deleteButton.props('loading')).toBe(false);
- expect(wrapper.emitted('deleted')).toEqual([[item.id]]);
- });
-
- it('shows the alert for the error', () => {
- const errorMsg = 'An error occurred';
- findDeleteModal().vm.$emit('error', errorMsg);
-
- expect(createAlert).toHaveBeenCalledWith({
- message: AUDIT_STREAMS_NETWORK_ERRORS.DELETING_ERROR,
- captureError: true,
- error: errorMsg,
- });
- });
- });
- });
-
- describe('destination event filters', () => {
- describe('renders', () => {
- beforeEach(() => {
- createComponent({
- mountFn: mountExtended,
- props: { item: mockInstanceExternalDestinations[1] },
- });
- });
-
- it('displays the correct text', () => {
- expect(findFilteringHeader().text()).toBe(ADD_STREAM_EDITOR_I18N.HEADER_FILTERING);
- expect(findEventTypeFilteringHeader().text()).toBe(
- ADD_STREAM_EDITOR_I18N.FILTER_BY_AUDIT_EVENT_TYPE,
- );
- });
-
- it('passes selected audit event types to StreamEventTypeFilters', () => {
- expect(findFilters().props()).toStrictEqual({
- value: mockInstanceExternalDestinations[1].eventTypeFilters,
- });
- });
- });
-
- describe('on change filters', () => {
- it('removes the deselected filters from a destination', async () => {
- const destinationUpdateSpy = jest
- .fn()
- .mockResolvedValue(destinationInstanceUpdateMutationPopulator());
-
- const filterRemoveSpy = jest
- .fn()
- .mockResolvedValue(destinationInstanceFilterRemoveMutationPopulator());
-
- createComponent({
- mountFn: mountExtended,
- props: { item: mockInstanceExternalDestinations[1] },
- apolloHandlers: [
- [instanceExternalAuditEventDestinationUpdate, destinationUpdateSpy],
- [deleteInstanceExternalDestinationFilters, filterRemoveSpy],
- ],
- });
-
- await findFilters().vm.$emit('input', mockRemoveFilterSelect);
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(false);
-
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(filterRemoveSpy).toHaveBeenCalledWith({
- destinationId: mockInstanceExternalDestinations[1].id,
- eventTypeFilters: mockRemoveFilterRemaining,
- });
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('updated')).toBeDefined();
- });
-
- it('adds the selected filters for a destination', async () => {
- const destinationUpdateSpy = jest
- .fn()
- .mockResolvedValue(destinationInstanceUpdateMutationPopulator());
-
- const filterAddSpy = jest
- .fn()
- .mockResolvedValue(destinationInstanceFilterUpdateMutationPopulator());
-
- createComponent({
- mountFn: mountExtended,
- props: { item: mockInstanceExternalDestinations[1] },
- apolloHandlers: [
- [instanceExternalAuditEventDestinationUpdate, destinationUpdateSpy],
- [addInstanceExternalDestinationFilters, filterAddSpy],
- ],
- });
-
- await findFilters().vm.$emit('input', mockAddFilterSelect);
-
- expect(findSubmitStreamBtn().props('disabled')).toBe(false);
-
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(filterAddSpy).toHaveBeenCalledWith({
- destinationId: mockInstanceExternalDestinations[1].id,
- eventTypeFilters: mockAddFilterRemaining,
- });
-
- expect(findAlertErrors()).toHaveLength(0);
- expect(wrapper.emitted('error')).toBeUndefined();
- expect(wrapper.emitted('updated')).toBeDefined();
- });
-
- it('should not emit updated event and reports error when network error occurs while saving', async () => {
- const sentryError = new Error('Network error');
- const sentryCaptureExceptionSpy = jest.spyOn(Sentry, 'captureException');
- const destinationUpdateSpy = jest
- .fn()
- .mockResolvedValue(destinationInstanceUpdateMutationPopulator());
- const filterRemoveSpy = jest.fn().mockRejectedValue(sentryError);
-
- createComponent({
- mountFn: mountExtended,
- props: { item: mockInstanceExternalDestinations[1] },
- apolloHandlers: [
- [instanceExternalAuditEventDestinationUpdate, destinationUpdateSpy],
- [deleteInstanceExternalDestinationFilters, filterRemoveSpy],
- ],
- });
-
- findFilters().vm.$emit('input', mockRemoveFilterSelect);
-
- findDestinationForm().vm.$emit('submit', { preventDefault: () => {} });
- await waitForPromises();
-
- expect(findAlertErrors()).toHaveLength(1);
- expect(findAlertErrors().at(0).text()).toBe(AUDIT_STREAMS_NETWORK_ERRORS.UPDATING_ERROR);
- expect(sentryCaptureExceptionSpy).toHaveBeenCalledWith(sentryError);
- expect(wrapper.emitted('error')).toBeDefined();
- expect(wrapper.emitted('updated')).toBeUndefined();
- });
- });
- });
- });
-});
diff --git a/ee/spec/frontend/audit_events/components/stream/stream_item_spec.js b/ee/spec/frontend/audit_events/components/stream/stream_item_spec.js
index 3a521ac85fb82456e58c9a9591fe6aabe31e38a1..0e2735b9dc3f60eb7c9bfca8b60fd8570bbe5957 100644
--- a/ee/spec/frontend/audit_events/components/stream/stream_item_spec.js
+++ b/ee/spec/frontend/audit_events/components/stream/stream_item_spec.js
@@ -9,29 +9,11 @@ import * as Sentry from '~/sentry/sentry_browser_wrapper';
import { STREAM_ITEMS_I18N, UPDATE_STREAM_MESSAGE } from 'ee/audit_events/constants';
import StreamItem from 'ee/audit_events/components/stream/stream_item.vue';
import StreamDestinationEditor from 'ee/audit_events/components/stream/stream_destination_editor.vue';
-import StreamHttpDestinationEditor from 'ee/audit_events/components/stream/stream_http_destination_editor.vue';
-import StreamGcpLoggingDestinationEditor from 'ee/audit_events/components/stream/stream_gcp_logging_destination_editor.vue';
-import StreamAmazonS3DestinationEditor from 'ee/audit_events/components/stream/stream_amazon_s3_destination_editor.vue';
import groupAuditEventStreamingDestinationsUpdate from 'ee/audit_events/graphql/mutations/update_group_streaming_destination.mutation.graphql';
import instanceAuditEventStreamingDestinationsUpdate from 'ee/audit_events/graphql/mutations/update_instance_streaming_destination.mutation.graphql';
-import externalAuditEventDestinationUpdate from 'ee/audit_events/graphql/mutations/update_external_destination.mutation.graphql';
-import instanceExternalAuditEventDestinationUpdate from 'ee/audit_events/graphql/mutations/update_instance_external_destination.mutation.graphql';
-import googleCloudLoggingConfigurationUpdate from 'ee/audit_events/graphql/mutations/update_gcp_logging_destination.mutation.graphql';
-import instanceGoogleCloudLoggingConfigurationUpdate from 'ee/audit_events/graphql/mutations/update_instance_gcp_logging_destination.mutation.graphql';
-import amazonS3ConfigurationUpdate from 'ee/audit_events/graphql/mutations/update_amazon_s3_destination.mutation.graphql';
-import instanceAmazonS3ConfigurationUpdate from 'ee/audit_events/graphql/mutations/update_instance_amazon_s3_destination.mutation.graphql';
-
-import {
- groupPath,
- instanceGroupPath,
- mockExternalDestinations,
- mockInstanceExternalDestinations,
- mockHttpType,
- mockGcpLoggingType,
- mockAmazonS3Type,
- mockAmazonS3Destinations,
-} from '../../mock_data';
+
+import { groupPath, instanceGroupPath } from '../../mock_data';
import { mockHttpTypeDestination } from '../../mock_data/consolidated_api';
jest.mock('~/alert');
@@ -43,24 +25,14 @@ describe('StreamItem', () => {
let wrapper;
let mutationHandlers;
- const destinationWithoutFilters = mockExternalDestinations[0];
- const destinationWithFilters = mockExternalDestinations[1];
- const instanceDestination = mockInstanceExternalDestinations[0];
-
- let groupPathProvide = groupPath;
- let itemProps = destinationWithoutFilters;
- let typeProps = mockHttpType;
+ const groupPathProvide = groupPath;
+ const itemProps = mockHttpTypeDestination[0];
+ const typeProps = 'http'; // Type is required by component but not used in consolidated API
beforeEach(() => {
mutationHandlers = {
groupStreamingUpdate: jest.fn(),
instanceStreamingUpdate: jest.fn(),
- externalDestinationUpdate: jest.fn(),
- instanceExternalUpdate: jest.fn(),
- gcpLoggingUpdate: jest.fn(),
- instanceGcpLoggingUpdate: jest.fn(),
- amazonS3Update: jest.fn(),
- instanceAmazonS3Update: jest.fn(),
};
});
@@ -68,12 +40,6 @@ describe('StreamItem', () => {
const apolloProvider = createMockApollo([
[groupAuditEventStreamingDestinationsUpdate, mutationHandlers.groupStreamingUpdate],
[instanceAuditEventStreamingDestinationsUpdate, mutationHandlers.instanceStreamingUpdate],
- [externalAuditEventDestinationUpdate, mutationHandlers.externalDestinationUpdate],
- [instanceExternalAuditEventDestinationUpdate, mutationHandlers.instanceExternalUpdate],
- [googleCloudLoggingConfigurationUpdate, mutationHandlers.gcpLoggingUpdate],
- [instanceGoogleCloudLoggingConfigurationUpdate, mutationHandlers.instanceGcpLoggingUpdate],
- [amazonS3ConfigurationUpdate, mutationHandlers.amazonS3Update],
- [instanceAmazonS3ConfigurationUpdate, mutationHandlers.instanceAmazonS3Update],
]);
wrapper = mountExtended(StreamItem, {
@@ -89,9 +55,6 @@ describe('StreamItem', () => {
apolloProvider,
stubs: {
StreamDestinationEditor: true,
- StreamHttpDestinationEditor: true,
- StreamGcpLoggingDestinationEditor: true,
- StreamAmazonS3DestinationEditor: true,
},
});
};
@@ -99,19 +62,13 @@ describe('StreamItem', () => {
const findToggleButton = () => wrapper.findByTestId('toggle-btn');
const findToggle = () => wrapper.findComponent(GlToggle);
const findStreamDestinationEditor = () => wrapper.findComponent(StreamDestinationEditor);
- const findStreamHttpDestinationEditor = () => wrapper.findComponent(StreamHttpDestinationEditor);
const findAlert = () => wrapper.findComponent(GlAlert);
- const findGcpLoggingEditor = () => wrapper.findComponent(StreamGcpLoggingDestinationEditor);
- const findAmazonS3Editor = () => wrapper.findComponent(StreamAmazonS3DestinationEditor);
const findFilterBadge = () => wrapper.findByTestId('filter-badge');
- describe('when useConsolidatedAuditEventStreamDestApi is enabled', () => {
+ describe('Group StreamItem', () => {
beforeEach(async () => {
createComponent({
props: { item: mockHttpTypeDestination[0] },
- provide: {
- glFeatures: { useConsolidatedAuditEventStreamDestApi: true },
- },
});
await findToggleButton().vm.$emit('click');
});
@@ -124,9 +81,6 @@ describe('StreamItem', () => {
it('renders toggle with correct state', () => {
createComponent({
props: { item: mockHttpTypeDestination[0] },
- provide: {
- glFeatures: { useConsolidatedAuditEventStreamDestApi: true },
- },
});
expect(findToggle().exists()).toBe(true);
@@ -137,9 +91,6 @@ describe('StreamItem', () => {
it('calls consolidated API mutation for group destinations', async () => {
createComponent({
props: { item: mockHttpTypeDestination[0] },
- provide: {
- glFeatures: { useConsolidatedAuditEventStreamDestApi: true },
- },
});
mutationHandlers.groupStreamingUpdate.mockResolvedValue({
@@ -173,7 +124,6 @@ describe('StreamItem', () => {
},
provide: {
groupPath: instanceGroupPath,
- glFeatures: { useConsolidatedAuditEventStreamDestApi: true },
},
});
@@ -195,16 +145,14 @@ describe('StreamItem', () => {
});
});
});
- });
- describe('Group http StreamItem', () => {
describe('render', () => {
beforeEach(() => {
createComponent();
});
it('should not render the editor', () => {
- expect(findStreamHttpDestinationEditor().isVisible()).toBe(false);
+ expect(findStreamDestinationEditor().isVisible()).toBe(false);
});
it('renders toggle with active state', () => {
@@ -216,7 +164,7 @@ describe('StreamItem', () => {
it('renders toggle with inactive state', () => {
createComponent({
props: {
- item: { ...destinationWithoutFilters, active: false },
+ item: { ...mockHttpTypeDestination[0], active: false },
},
});
@@ -227,7 +175,7 @@ describe('StreamItem', () => {
it('applies opacity class when destination is inactive', () => {
createComponent({
props: {
- item: { ...destinationWithoutFilters, active: false },
+ item: { ...mockHttpTypeDestination[0], active: false },
},
});
@@ -248,7 +196,7 @@ describe('StreamItem', () => {
async ({ error, expectedResult }) => {
createComponent();
- mutationHandlers.externalDestinationUpdate.mockRejectedValue(error);
+ mutationHandlers.groupStreamingUpdate.mockRejectedValue(error);
await findToggle().vm.$emit('change', true);
await waitForPromises();
@@ -262,6 +210,7 @@ describe('StreamItem', () => {
},
);
});
+
describe('deleting', () => {
const id = 1;
@@ -269,7 +218,7 @@ describe('StreamItem', () => {
createComponent();
await findToggleButton().vm.$emit('click');
- findStreamHttpDestinationEditor().vm.$emit('deleted', id);
+ findStreamDestinationEditor().vm.$emit('deleted', id);
expect(wrapper.emitted('deleted')).toEqual([[id]]);
});
@@ -282,186 +231,52 @@ describe('StreamItem', () => {
});
it('should pass the item to the editor', () => {
- expect(findStreamHttpDestinationEditor().exists()).toBe(true);
- expect(findStreamHttpDestinationEditor().props('item')).toStrictEqual(
- mockExternalDestinations[0],
+ expect(findStreamDestinationEditor().exists()).toBe(true);
+ expect(findStreamDestinationEditor().props('item')).toStrictEqual(
+ mockHttpTypeDestination[0],
);
});
it('should emit the updated event and show success message when the editor fires its update event', async () => {
- await findStreamHttpDestinationEditor().vm.$emit('updated');
+ await findStreamDestinationEditor().vm.$emit('updated');
expect(findAlert().text()).toBe(UPDATE_STREAM_MESSAGE);
expect(wrapper.emitted('updated')).toBeDefined();
- expect(findStreamHttpDestinationEditor().exists()).toBe(true);
+ expect(findStreamDestinationEditor().exists()).toBe(true);
});
it('should emit the error event when the editor fires its error event', () => {
- findStreamHttpDestinationEditor().vm.$emit('error');
+ findStreamDestinationEditor().vm.$emit('error');
expect(wrapper.emitted('error')).toBeDefined();
- expect(findStreamHttpDestinationEditor().exists()).toBe(true);
+ expect(findStreamDestinationEditor().exists()).toBe(true);
});
it('should close the editor when the editor fires its cancel event', async () => {
- findStreamHttpDestinationEditor().vm.$emit('cancel');
+ findStreamDestinationEditor().vm.$emit('cancel');
await waitForPromises();
- expect(findStreamHttpDestinationEditor().isVisible()).toBe(false);
+ expect(findStreamDestinationEditor().isVisible()).toBe(false);
});
it('clears success message when closing', async () => {
- await findStreamHttpDestinationEditor().vm.$emit('updated');
+ await findStreamDestinationEditor().vm.$emit('updated');
await findToggleButton().vm.$emit('click');
expect(findAlert().exists()).toBe(false);
});
});
- describe('active toggle', () => {
- it('successfully toggles destination to inactive', async () => {
- createComponent();
-
- mutationHandlers.externalDestinationUpdate.mockResolvedValue({
- data: {
- externalAuditEventDestinationUpdate: {
- errors: [],
- externalAuditEventDestination: {
- ...destinationWithoutFilters,
- active: false,
- },
- },
- },
- });
-
- await findToggle().vm.$emit('change', false);
- await waitForPromises();
-
- expect(mutationHandlers.externalDestinationUpdate).toHaveBeenCalledWith({
- id: destinationWithoutFilters.id,
- name: destinationWithoutFilters.name,
- active: false,
- });
-
- const emitted = wrapper.emitted();
- if (emitted.updated) {
- expect(emitted.updated).toHaveLength(1);
- }
-
- const alert = findAlert();
- if (alert.exists()) {
- expect(alert.text()).toBe('Destination deactivated successfully.');
- }
- });
-
- it('successfully toggles destination to active', async () => {
- createComponent({
- props: {
- item: { ...destinationWithoutFilters, active: false },
- },
- });
-
- mutationHandlers.externalDestinationUpdate.mockResolvedValue({
- data: {
- externalAuditEventDestinationUpdate: {
- errors: [],
- externalAuditEventDestination: {
- ...destinationWithoutFilters,
- active: true,
- },
- },
- },
- });
-
- await findToggle().vm.$emit('change', true);
- await waitForPromises();
-
- const emitted = wrapper.emitted();
- if (emitted.updated) {
- expect(emitted.updated).toHaveLength(1);
- }
-
- const alert = findAlert();
- if (alert.exists()) {
- expect(alert.text()).toBe('Destination activated successfully.');
- }
- });
-
- it('handles GraphQL mutation errors', async () => {
- createComponent();
-
- mutationHandlers.externalDestinationUpdate.mockResolvedValue({
- data: {
- externalAuditEventDestinationUpdate: {
- errors: ['Something went wrong'],
- externalAuditEventDestination: null,
- },
- },
- });
-
- await findToggle().vm.$emit('change', false);
- await waitForPromises();
-
- expect(Sentry.captureException).toHaveBeenCalled();
- expect(createAlert).toHaveBeenCalledWith({
- message: 'Failed to update destination status. Please try again.',
- captureError: true,
- error: expect.any(Error),
- });
- });
-
- it('handles network errors', async () => {
- createComponent();
-
- const error = new Error('Network error');
- mutationHandlers.externalDestinationUpdate.mockRejectedValue(error);
-
- await findToggle().vm.$emit('change', false);
- await waitForPromises();
-
- expect(Sentry.captureException).toHaveBeenCalledWith(error);
- expect(createAlert).toHaveBeenCalledWith({
- message: 'Failed to update destination status. Please try again.',
- captureError: true,
- error,
- });
- });
-
- it('shows loading state during toggle operation', async () => {
- createComponent();
-
- mutationHandlers.externalDestinationUpdate.mockImplementation(() => new Promise(() => {}));
-
- await findToggle().vm.$emit('change', false);
-
- expect(findToggle().props('isLoading')).toBe(true);
- expect(findToggle().props('disabled')).toBe(true);
- });
- });
-
describe('when an item has event filters', () => {
beforeEach(() => {
- createComponent({ props: { item: destinationWithFilters } });
- });
-
- it('should show filter badge', () => {
- expect(findFilterBadge().text()).toBe(STREAM_ITEMS_I18N.FILTER_BADGE_LABEL);
- expect(findFilterBadge().attributes('id')).toBe(destinationWithFilters.id);
- });
-
- it('renders a popover', () => {
- expect(wrapper.findByTestId('filter-popover').element).toMatchSnapshot();
- });
- });
-
- describe('when an item has namespace filters', () => {
- beforeEach(() => {
- createComponent({ props: { item: destinationWithFilters } });
+ createComponent({
+ props: { item: { ...mockHttpTypeDestination[0], eventTypeFilters: ['user_created'] } },
+ });
});
it('should show filter badge', () => {
expect(findFilterBadge().text()).toBe(STREAM_ITEMS_I18N.FILTER_BADGE_LABEL);
- expect(findFilterBadge().attributes('id')).toBe(destinationWithFilters.id);
+ expect(findFilterBadge().attributes('id')).toBe(mockHttpTypeDestination[0].id);
});
it('renders a popover', () => {
@@ -471,7 +286,11 @@ describe('StreamItem', () => {
describe('when an item has no filter', () => {
beforeEach(() => {
- createComponent();
+ createComponent({
+ props: {
+ item: { ...mockHttpTypeDestination[0], eventTypeFilters: [], namespaceFilter: null },
+ },
+ });
});
it('should not show filter badge', () => {
@@ -486,402 +305,11 @@ describe('StreamItem', () => {
expect(findToggle().props('value')).toBe(true);
await wrapper.setProps({
- item: { ...destinationWithoutFilters, active: false },
+ item: { ...mockHttpTypeDestination[0], active: false },
});
expect(findToggle().props('value')).toBe(false);
});
});
});
-
- describe('Group gcp logging StreamItem', () => {
- beforeEach(() => {
- typeProps = mockGcpLoggingType;
- itemProps = {
- id: 'test_id1',
- name: 'Name',
- googleProjectIdName: 'test-project',
- clientEmail: 'test@example.com',
- logIdName: 'audit_events',
- active: true,
- };
- });
-
- describe('render', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('should not render the editor', () => {
- expect(findGcpLoggingEditor().isVisible()).toBe(false);
- });
-
- it('renders toggle with correct state', () => {
- expect(findToggle().exists()).toBe(true);
- expect(findToggle().props('value')).toBe(true);
- });
- });
-
- describe('deleting', () => {
- const id = 1;
-
- it('bubbles up the "deleted" event', async () => {
- createComponent();
- await findToggleButton().vm.$emit('click');
-
- findGcpLoggingEditor().vm.$emit('deleted', id);
-
- expect(wrapper.emitted('deleted')).toEqual([[id]]);
- });
- });
-
- describe('editing', () => {
- beforeEach(async () => {
- createComponent();
- await findToggleButton().vm.$emit('click');
- });
-
- it('should pass the item to the editor', () => {
- expect(findGcpLoggingEditor().exists()).toBe(true);
- expect(findGcpLoggingEditor().props('item')).toStrictEqual(itemProps);
- });
-
- it('should emit the updated event and show success message when the editor fires its update event', async () => {
- await findGcpLoggingEditor().vm.$emit('updated');
-
- expect(wrapper.emitted('updated')).toBeDefined();
- expect(findAlert().text()).toBe(UPDATE_STREAM_MESSAGE);
- expect(findGcpLoggingEditor().exists()).toBe(true);
- });
-
- it('should emit the error event when the editor fires its error event', () => {
- findGcpLoggingEditor().vm.$emit('error');
-
- expect(wrapper.emitted('error')).toBeDefined();
- expect(findGcpLoggingEditor().exists()).toBe(true);
- });
-
- it('should close the editor when the editor fires its cancel event', async () => {
- findGcpLoggingEditor().vm.$emit('cancel');
- await waitForPromises();
-
- expect(findGcpLoggingEditor().isVisible()).toBe(false);
- });
-
- it('clears success message when closing', async () => {
- await findGcpLoggingEditor().vm.$emit('updated');
- await findToggleButton().vm.$emit('click');
-
- expect(findAlert().exists()).toBe(false);
- });
- });
-
- describe('active toggle', () => {
- it('handles GCP Logging destination toggle', async () => {
- createComponent();
-
- mutationHandlers.gcpLoggingUpdate.mockResolvedValue({
- data: {
- auditEventsGoogleCloudLoggingConfigurationUpdate: {
- errors: [],
- googleCloudLoggingConfiguration: {
- ...itemProps,
- active: false,
- },
- },
- },
- });
-
- await findToggle().vm.$emit('change', false);
- await waitForPromises();
-
- expect(mutationHandlers.gcpLoggingUpdate).toHaveBeenCalledWith({
- id: itemProps.id,
- name: itemProps.name,
- active: false,
- logIdName: expect.any(String),
- googleProjectIdName: expect.any(String),
- clientEmail: expect.any(String),
- });
- });
-
- it('handles errors for GCP Logging destinations', async () => {
- createComponent();
-
- const error = new Error('GCP error');
- mutationHandlers.gcpLoggingUpdate.mockRejectedValue(error);
-
- await findToggle().vm.$emit('change', false);
- await waitForPromises();
-
- expect(Sentry.captureException).toHaveBeenCalledWith(error);
- expect(createAlert).toHaveBeenCalled();
- });
- });
- });
-
- describe('Group Amazon S3 StreamItem', () => {
- beforeEach(() => {
- typeProps = mockAmazonS3Type;
- [itemProps] = mockAmazonS3Destinations;
- });
-
- describe('render', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('should not render the editor', () => {
- expect(findAmazonS3Editor().isVisible()).toBe(false);
- });
-
- it('renders toggle', () => {
- expect(findToggle().exists()).toBe(true);
- });
- });
-
- describe('deleting', () => {
- const id = 1;
-
- it('bubbles up the "deleted" event', async () => {
- createComponent();
- await findToggleButton().vm.$emit('click');
-
- findAmazonS3Editor().vm.$emit('deleted', id);
-
- expect(wrapper.emitted('deleted')).toEqual([[id]]);
- });
- });
-
- describe('editing', () => {
- beforeEach(async () => {
- createComponent();
- await findToggleButton().vm.$emit('click');
- });
-
- it('should pass the item to the editor', () => {
- expect(findAmazonS3Editor().exists()).toBe(true);
- expect(findAmazonS3Editor().props('item')).toStrictEqual(itemProps);
- });
-
- it('should emit the updated event and show success message when the editor fires its update event', async () => {
- await findAmazonS3Editor().vm.$emit('updated');
-
- expect(wrapper.emitted('updated')).toBeDefined();
- expect(findAlert().text()).toBe(UPDATE_STREAM_MESSAGE);
- expect(findAmazonS3Editor().exists()).toBe(true);
- });
-
- it('should emit the error event when the editor fires its error event', () => {
- findAmazonS3Editor().vm.$emit('error');
-
- expect(wrapper.emitted('error')).toBeDefined();
- expect(findAmazonS3Editor().exists()).toBe(true);
- });
-
- it('should close the editor when the editor fires its cancel event', async () => {
- findAmazonS3Editor().vm.$emit('cancel');
- await waitForPromises();
-
- expect(findAmazonS3Editor().isVisible()).toBe(false);
- });
-
- it('clears success message when closing', async () => {
- await findAmazonS3Editor().vm.$emit('updated');
- await findToggleButton().vm.$emit('click');
-
- expect(findAlert().exists()).toBe(false);
- });
- });
-
- describe('active toggle', () => {
- it('handles Amazon S3 destination toggle', async () => {
- createComponent();
-
- mutationHandlers.amazonS3Update.mockResolvedValue({
- data: {
- auditEventsAmazonS3ConfigurationUpdate: {
- errors: [],
- amazonS3Configuration: {
- ...itemProps,
- active: false,
- },
- },
- },
- });
-
- await findToggle().vm.$emit('change', false);
- await waitForPromises();
-
- expect(mutationHandlers.amazonS3Update).toHaveBeenCalledWith({
- id: itemProps.id,
- name: itemProps.name,
- active: false,
- fullPath: groupPath,
- bucketName: expect.any(String),
- awsRegion: expect.any(String),
- accessKeyXid: expect.any(String),
- });
- });
- });
- });
-
- describe('Instance StreamItem', () => {
- beforeEach(() => {
- groupPathProvide = instanceGroupPath;
- itemProps = instanceDestination;
- typeProps = mockHttpType;
- });
-
- describe('render', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('should not render the editor', () => {
- expect(findStreamHttpDestinationEditor().isVisible()).toBe(false);
- });
-
- it('renders toggle', () => {
- expect(findToggle().exists()).toBe(true);
- });
- });
-
- describe('deleting', () => {
- const id = 1;
-
- it('bubbles up the "deleted" event', async () => {
- createComponent();
- await findToggleButton().vm.$emit('click');
-
- findStreamHttpDestinationEditor().vm.$emit('deleted', id);
-
- expect(wrapper.emitted('deleted')).toEqual([[id]]);
- });
- });
-
- describe('editing', () => {
- beforeEach(async () => {
- createComponent();
- await findToggleButton().vm.$emit('click');
- });
-
- it('should pass the item to the editor', () => {
- expect(findStreamHttpDestinationEditor().exists()).toBe(true);
- expect(findStreamHttpDestinationEditor().props('item')).toStrictEqual(
- mockInstanceExternalDestinations[0],
- );
- });
-
- it('should emit the updated event and show success message when the editor fires its update event', async () => {
- await findStreamHttpDestinationEditor().vm.$emit('updated');
-
- expect(findAlert().text()).toBe(UPDATE_STREAM_MESSAGE);
- expect(wrapper.emitted('updated')).toBeDefined();
- expect(findStreamHttpDestinationEditor().exists()).toBe(true);
- });
-
- it('should emit the error event when the editor fires its error event', () => {
- findStreamHttpDestinationEditor().vm.$emit('error');
-
- expect(wrapper.emitted('error')).toBeDefined();
- expect(findStreamHttpDestinationEditor().exists()).toBe(true);
- });
-
- it('should close the editor when the editor fires its cancel event', async () => {
- findStreamHttpDestinationEditor().vm.$emit('cancel');
- await waitForPromises();
-
- expect(findStreamHttpDestinationEditor().isVisible()).toBe(false);
- });
-
- it('clears success message when closing', async () => {
- await findStreamHttpDestinationEditor().vm.$emit('updated');
- await findToggleButton().vm.$emit('click');
-
- expect(findAlert().exists()).toBe(false);
- });
- });
-
- describe('active toggle', () => {
- it('toggles instance destination active state', async () => {
- createComponent();
-
- mutationHandlers.instanceExternalUpdate.mockResolvedValue({
- data: {
- instanceExternalAuditEventDestinationUpdate: {
- errors: [],
- instanceExternalAuditEventDestination: {
- ...instanceDestination,
- active: false,
- },
- },
- },
- });
-
- await findToggle().vm.$emit('change', false);
- await waitForPromises();
-
- expect(mutationHandlers.instanceExternalUpdate).toHaveBeenCalledWith({
- id: instanceDestination.id,
- name: instanceDestination.name,
- active: false,
- });
- });
-
- it('handles instance GCP Logging destinations', async () => {
- typeProps = mockGcpLoggingType;
- createComponent();
-
- mutationHandlers.instanceGcpLoggingUpdate.mockResolvedValue({
- data: {
- instanceGoogleCloudLoggingConfigurationUpdate: {
- errors: [],
- instanceGoogleCloudLoggingConfiguration: {
- ...instanceDestination,
- active: false,
- },
- },
- },
- });
-
- await findToggle().vm.$emit('change', false);
- await waitForPromises();
-
- expect(mutationHandlers.instanceGcpLoggingUpdate).toHaveBeenCalled();
- });
-
- it('handles instance Amazon S3 destinations', async () => {
- typeProps = mockAmazonS3Type;
- createComponent();
-
- mutationHandlers.instanceAmazonS3Update.mockResolvedValue({
- data: {
- auditEventsInstanceAmazonS3ConfigurationUpdate: {
- errors: [],
- instanceAmazonS3Configuration: {
- ...instanceDestination,
- active: false,
- },
- },
- },
- });
-
- await findToggle().vm.$emit('change', false);
- await waitForPromises();
-
- expect(mutationHandlers.instanceAmazonS3Update).toHaveBeenCalled();
- });
- });
-
- describe('when an item has no filter', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('should not show filter badge', () => {
- expect(findFilterBadge().exists()).toBe(false);
- });
- });
- });
});
diff --git a/ee/spec/graphql/mutations/audit_events/streaming/event_type_filters/create_spec.rb b/ee/spec/graphql/mutations/audit_events/streaming/event_type_filters/create_spec.rb
deleted file mode 100644
index 133ec35b0e5524a1b3098f5e44ea9b236ed456d8..0000000000000000000000000000000000000000
--- a/ee/spec/graphql/mutations/audit_events/streaming/event_type_filters/create_spec.rb
+++ /dev/null
@@ -1,82 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Mutations::AuditEvents::Streaming::EventTypeFilters::Create, feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:current_user) { create(:user) }
- let_it_be(:destination) { create(:external_audit_event_destination) }
-
- let(:group) { destination.group }
- let(:mutation) { described_class.new(object: nil, context: query_context, field: nil) }
- let(:params) do
- {
- destination_id: destination.to_gid,
- event_type_filters: %w[filter_1]
- }
- end
-
- subject { mutation.resolve(**params) }
-
- describe '#resolve' do
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it 'returns useful error messages' do
- expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
- end
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current_user is not group owner' do
- it 'returns useful error messages' do
- expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
- end
- end
-
- context 'when current_user is group owner' do
- before do
- group.add_owner(current_user)
- end
-
- context 'and calls create service' do
- context 'when response is success' do
- let(:response) { ServiceResponse.success }
-
- before do
- allow_next_instance_of(::AuditEvents::Streaming::EventTypeFilters::CreateService) do |instance|
- allow(instance).to receive(:execute).and_return(response)
- end
- end
-
- it 'returns event type filters' do
- expect(subject).to eq({ event_type_filters: destination.event_type_filters, errors: [] })
- end
- end
-
- context 'when response is error' do
- let(:response) { ServiceResponse.error(message: 'Something went wrong') }
-
- before do
- allow_next_instance_of(::AuditEvents::Streaming::EventTypeFilters::CreateService) do |instance|
- allow(instance).to receive(:execute).and_return(response)
- end
- end
-
- it 'returns error message' do
- expect(subject).to eq({ event_type_filters: destination.event_type_filters,
- errors: ['Something went wrong'] })
- end
- end
- end
- end
- end
- end
-end
diff --git a/ee/spec/graphql/mutations/audit_events/streaming/event_type_filters/destroy_spec.rb b/ee/spec/graphql/mutations/audit_events/streaming/event_type_filters/destroy_spec.rb
deleted file mode 100644
index 795af6cfb38d9da0d9e275505a1f67b40d261a90..0000000000000000000000000000000000000000
--- a/ee/spec/graphql/mutations/audit_events/streaming/event_type_filters/destroy_spec.rb
+++ /dev/null
@@ -1,139 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Mutations::AuditEvents::Streaming::EventTypeFilters::Destroy, feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:current_user) { create(:user) }
- let_it_be(:event_type_filter) do
- create(:audit_events_streaming_event_type_filter, audit_event_type: 'event_type_filters_created')
- end
-
- let(:destination) { event_type_filter.external_audit_event_destination }
- let(:group) { destination.group }
- let(:mutation) { described_class.new(object: nil, context: query_context, field: nil) }
- let(:params) do
- {
- destination_id: destination.to_gid,
- event_type_filters: %w[event_type_filters_created]
- }
- end
-
- subject { mutation.resolve(**params) }
-
- describe '#resolve' do
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it 'when user is not authorized' do
- expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
- end
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current_user is not group owner' do
- it 'returns useful error messages' do
- expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable, 'The resource that you '\
- 'are attempting to access does not exist or you don\'t have permission to perform this action')
- end
- end
-
- context 'when current_user is group owner' do
- before do
- group.add_owner(current_user)
- end
-
- context 'when event type filter is present' do
- it 'deletes the event type filter', :aggregate_failures do
- expect { subject }.to change { destination.event_type_filters.count }.by(-1)
- expect(subject).to eq({ errors: [] })
- end
-
- it 'creates an audit event' do
- expect { subject }.to change { AuditEvent.count }.by(1)
-
- audit_event = AuditEvent.last
- expect(audit_event.author).to eq(current_user)
- expect(audit_event.details[:custom_message]).to include('Deleted audit event type filter(s)')
- end
- end
-
- context 'when deleting multiple event type filters' do
- let(:second_filter) do
- create(:audit_events_streaming_event_type_filter,
- audit_event_type: 'member_created',
- external_audit_event_destination: destination)
- end
-
- let(:params) do
- {
- destination_id: destination.to_gid,
- event_type_filters: %w[event_type_filters_created member_created]
- }
- end
-
- before do
- second_filter
- end
-
- it 'deletes all specified filters', :aggregate_failures do
- expect { subject }.to change { destination.event_type_filters.count }.by(-2)
- expect(subject).to eq({ errors: [] })
- expect(destination.event_type_filters.pluck(:audit_event_type)).to be_empty
- end
- end
-
- context 'when event type filter is not already present' do
- let(:params) do
- {
- destination_id: destination.to_gid,
- event_type_filters: %w[event_type_filters_deleted]
- }
- end
-
- it 'does not delete event type filter', :aggregate_failures do
- expect { subject }.not_to change { destination.event_type_filters.count }
- expect(subject)
- .to eq(
- {
- errors: [
- "Couldn't find event type filters where audit event type(s): event_type_filters_deleted"
- ]
- }
- )
- end
- end
-
- context 'with sync to streaming destination' do
- let(:stream_destination) do
- create(:audit_events_group_external_streaming_destination, group: group)
- end
-
- before do
- destination.update!(stream_destination: stream_destination)
- create(:audit_events_group_event_type_filters,
- audit_event_type: 'event_type_filters_created',
- external_streaming_destination: stream_destination,
- namespace: group)
- end
-
- context 'when sync is enabled' do
- it 'deletes filter from both destinations', :aggregate_failures do
- expect { subject }.to change { destination.event_type_filters.count }.by(-1)
- .and change { stream_destination.event_type_filters.count }.by(-1)
-
- expect(subject).to eq({ errors: [] })
- end
- end
- end
- end
- end
- end
-end
diff --git a/ee/spec/graphql/mutations/audit_events/streaming/headers/create_spec.rb b/ee/spec/graphql/mutations/audit_events/streaming/headers/create_spec.rb
deleted file mode 100644
index 129a9e2b8d203b1645bd7ca65c374e0766d92dae..0000000000000000000000000000000000000000
--- a/ee/spec/graphql/mutations/audit_events/streaming/headers/create_spec.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Mutations::AuditEvents::Streaming::Headers::Create, feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:current_user) { create(:user) }
- let_it_be(:destination) { create(:external_audit_event_destination) }
-
- let(:group) { destination.group }
- let(:mutation) { described_class.new(object: nil, context: query_context, field: nil) }
- let(:params) do
- {
- destination_id: destination.to_gid,
- key: 'foo',
- value: 'bar',
- active: true
- }
- end
-
- subject { mutation.resolve(**params) }
-
- describe '#resolve' do
- context 'feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it 'is not authorized' do
- expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
- end
- end
-
- context 'feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'current_user is not group owner' do
- it 'returns useful error messages' do
- expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
- end
- end
-
- context 'current_user is group owner' do
- before do
- group.add_owner(current_user)
- end
-
- it 'creates a new header' do
- expect { subject }.to change { destination.headers.count }.by 1
- end
- end
- end
- end
-end
diff --git a/ee/spec/graphql/mutations/audit_events/streaming/headers/destroy_spec.rb b/ee/spec/graphql/mutations/audit_events/streaming/headers/destroy_spec.rb
deleted file mode 100644
index 014c7693012b88737ee54d2a472cbbfb98a0bed9..0000000000000000000000000000000000000000
--- a/ee/spec/graphql/mutations/audit_events/streaming/headers/destroy_spec.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Mutations::AuditEvents::Streaming::Headers::Destroy do
- include GraphqlHelpers
-
- let_it_be(:current_user) { create(:user) }
- let_it_be(:header) { create(:audit_events_streaming_header) }
-
- let(:destination) { header.external_audit_event_destination }
- let(:group) { destination.group }
- let(:mutation) { described_class.new(object: nil, context: query_context, field: nil) }
-
- subject { mutation.resolve(**{ header_id: header.to_gid }) }
-
- describe '#resolve' do
- context 'feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it 'is not authorized' do
- expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable, 'The resource that you '\
- 'are attempting to access does not exist or you don\'t have permission to perform this action')
- end
- end
-
- context 'feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'current_user is not group owner' do
- it 'returns useful error messages' do
- expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable, 'The resource that you '\
- 'are attempting to access does not exist or you don\'t have permission to perform this action')
- end
- end
-
- context 'current_user is group owner' do
- before do
- group.add_owner(current_user)
- end
-
- it 'deletes the header' do
- expect { subject }.to change { destination.headers.count }.by(-1)
- end
-
- context 'when destroy fails' do
- before do
- allow_next_found_instance_of(AuditEvents::Streaming::Header) do |header|
- allow(header).to receive(:destroy).and_return(false)
- end
- end
-
- it 'does not delete any headers' do
- expect { subject }.not_to change { destination.headers.count }
- end
- end
- end
- end
- end
-end
diff --git a/ee/spec/graphql/types/audit_events/exterrnal_audit_event_destination_type_spec.rb b/ee/spec/graphql/types/audit_events/exterrnal_audit_event_destination_type_spec.rb
deleted file mode 100644
index 3e1abaedc2d6308954ec8e0e04330251f2f9bbd3..0000000000000000000000000000000000000000
--- a/ee/spec/graphql/types/audit_events/exterrnal_audit_event_destination_type_spec.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe GitlabSchema.types['ExternalAuditEventDestination'], feature_category: :audit_events do
- let(:fields) do
- %i[id destination_url group verification_token headers event_type_filters name namespace_filter active]
- end
-
- specify { expect(described_class.graphql_name).to eq('ExternalAuditEventDestination') }
- specify { expect(described_class).to have_graphql_fields(fields) }
- specify { expect(described_class).to require_graphql_authorizations(:admin_external_audit_events) }
-end
diff --git a/ee/spec/graphql/types/audit_events/google_cloud_logging_configuration_type_spec.rb b/ee/spec/graphql/types/audit_events/google_cloud_logging_configuration_type_spec.rb
deleted file mode 100644
index e424917376e1516d4a18fc4611ec5a6c3641c8a8..0000000000000000000000000000000000000000
--- a/ee/spec/graphql/types/audit_events/google_cloud_logging_configuration_type_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe GitlabSchema.types['GoogleCloudLoggingConfigurationType'], feature_category: :audit_events do
- let(:fields) do
- %i[id google_project_id_name client_email log_id_name name group active]
- end
-
- specify { expect(described_class.graphql_name).to eq('GoogleCloudLoggingConfigurationType') }
- specify { expect(described_class).to have_graphql_fields(fields) }
- # We do not return the private_key for security reasons.
- specify { expect(described_class).not_to have_graphql_fields('private_key') }
- specify { expect(described_class).to require_graphql_authorizations(:admin_external_audit_events) }
-end
diff --git a/ee/spec/graphql/types/audit_events/instance_external_audit_event_destination_type_spec.rb b/ee/spec/graphql/types/audit_events/instance_external_audit_event_destination_type_spec.rb
deleted file mode 100644
index cd2e101018343acfb53e65725674b236ce555f4a..0000000000000000000000000000000000000000
--- a/ee/spec/graphql/types/audit_events/instance_external_audit_event_destination_type_spec.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe GitlabSchema.types['InstanceExternalAuditEventDestination'], feature_category: :audit_events do
- let(:fields) do
- %i[id destination_url verification_token headers event_type_filters name active]
- end
-
- specify { expect(described_class.graphql_name).to eq('InstanceExternalAuditEventDestination') }
- specify { expect(described_class).to have_graphql_fields(fields) }
- specify { expect(described_class).to require_graphql_authorizations(:admin_instance_external_audit_events) }
-end
diff --git a/ee/spec/graphql/types/audit_events/streaming/header_type_spec.rb b/ee/spec/graphql/types/audit_events/streaming/header_type_spec.rb
deleted file mode 100644
index a1f529aff2e91c8923586a60f2082fc1e38aacb9..0000000000000000000000000000000000000000
--- a/ee/spec/graphql/types/audit_events/streaming/header_type_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe GitlabSchema.types['AuditEventStreamingHeader'], feature_category: :audit_events do
- let(:fields) do
- %i[id key value active]
- end
-
- specify { expect(described_class.graphql_name).to eq('AuditEventStreamingHeader') }
- specify { expect(described_class).to have_graphql_fields(fields) }
-end
diff --git a/ee/spec/graphql/types/audit_events/streaming/http/namespace_filter_type_spec.rb b/ee/spec/graphql/types/audit_events/streaming/http/namespace_filter_type_spec.rb
deleted file mode 100644
index 80424c7cf3b7e969f65a657243efd4b7bfa1ba35..0000000000000000000000000000000000000000
--- a/ee/spec/graphql/types/audit_events/streaming/http/namespace_filter_type_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe GitlabSchema.types['AuditEventStreamingHTTPNamespaceFilter'], feature_category: :audit_events do
- let(:fields) do
- %i[id namespace external_audit_event_destination]
- end
-
- specify { expect(described_class.graphql_name).to eq('AuditEventStreamingHTTPNamespaceFilter') }
- specify { expect(described_class).to have_graphql_fields(fields) }
-end
diff --git a/ee/spec/graphql/types/audit_events/streaming/instance_header_type_spec.rb b/ee/spec/graphql/types/audit_events/streaming/instance_header_type_spec.rb
deleted file mode 100644
index 968732ebb6ec0c08f8c25d6e10034ad75227bd4a..0000000000000000000000000000000000000000
--- a/ee/spec/graphql/types/audit_events/streaming/instance_header_type_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe GitlabSchema.types['AuditEventsStreamingInstanceHeader'], feature_category: :audit_events do
- let(:fields) do
- %i[id key value active]
- end
-
- specify { expect(described_class.graphql_name).to eq('AuditEventsStreamingInstanceHeader') }
- specify { expect(described_class).to have_graphql_fields(fields) }
-end
diff --git a/ee/spec/requests/api/graphql/audit_events/instance/amazon_s3_configuration_spec.rb b/ee/spec/requests/api/graphql/audit_events/instance/amazon_s3_configuration_spec.rb
deleted file mode 100644
index 3de839028d2d7520bce5b0d82199d0f3b737b5d8..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/audit_events/instance/amazon_s3_configuration_spec.rb
+++ /dev/null
@@ -1,103 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'List audit event Amazon S3 destinations for the instance', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:admin) { create(:admin) }
- let_it_be(:user) { create(:user) }
- let_it_be(:destination_1) { create(:instance_amazon_s3_configuration) }
- let_it_be(:destination_2) { create(:instance_amazon_s3_configuration) }
-
- let(:path) { %i[audit_events_instance_amazon_s3_configurations nodes] }
-
- let(:query) do
- graphql_query_for(
- :audit_events_instance_amazon_s3_configurations
- )
- end
-
- shared_examples 'a request that returns no destinations' do
- it 'returns no destinations' do
- post_graphql(query, current_user: current_user)
-
- expect(graphql_data_at(:audit_events_instance_amazon_s3_configurations, :nodes)).to be_empty
- end
- end
-
- context 'when user is authenticated' do
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when user is instance admin' do
- it 'returns the instance audit event Amazon S3 configurations', :aggregate_failures do
- post_graphql(query, current_user: admin)
-
- expect(graphql_data_at(*path)).to contain_exactly(
- a_hash_including(
- 'bucketName' => destination_1.bucket_name,
- 'awsRegion' => destination_1.aws_region,
- 'accessKeyXid' => destination_1.access_key_xid,
- 'name' => destination_1.name
- ),
- a_hash_including(
- 'bucketName' => destination_2.bucket_name,
- 'awsRegion' => destination_2.aws_region,
- 'accessKeyXid' => destination_2.access_key_xid,
- 'name' => destination_2.name
- )
- )
-
- expect(graphql_data_at(*path))
- .to contain_exactly(
- hash_not_including('secretAccessKey'),
- hash_not_including('secretAccessKey')
- )
- end
- end
-
- context 'when user is not instance admin' do
- it_behaves_like 'a request that returns no destinations' do
- let(:current_user) { user }
- end
- end
- end
-
- context 'when feature is not licensed' do
- context 'when user is instance admin' do
- it_behaves_like 'a request that returns no destinations' do
- let(:current_user) { admin }
- end
- end
-
- context 'when user is not instance admin' do
- it_behaves_like 'a request that returns no destinations' do
- let(:current_user) { user }
- end
- end
- end
- end
-
- context 'when user is not authenticated' do
- let(:user) { nil }
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- it_behaves_like 'a request that returns no destinations' do
- let(:current_user) { user }
- end
- end
-
- context 'when feature is not licensed' do
- it_behaves_like 'a request that returns no destinations' do
- let(:current_user) { user }
- end
- end
- end
-end
diff --git a/ee/spec/requests/api/graphql/audit_events/instance/google_cloud_logging_configuration_spec.rb b/ee/spec/requests/api/graphql/audit_events/instance/google_cloud_logging_configuration_spec.rb
deleted file mode 100644
index 2ffa17ff563dd315586f250cf20fb7810afb924d..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/audit_events/instance/google_cloud_logging_configuration_spec.rb
+++ /dev/null
@@ -1,103 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'getting a list of external audit event destinations for the instance', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:admin) { create(:admin) }
- let_it_be(:user) { create(:user) }
- let_it_be(:destination_1) { create(:instance_google_cloud_logging_configuration) }
- let_it_be(:destination_2) { create(:instance_google_cloud_logging_configuration) }
-
- let(:path) { %i[instance_google_cloud_logging_configurations nodes] }
-
- let(:query) do
- graphql_query_for(
- :instance_google_cloud_logging_configurations
- )
- end
-
- shared_examples 'a request that returns no destinations' do
- it 'returns no destinations' do
- post_graphql(query, current_user: current_user)
-
- expect(graphql_data_at(:instance_google_cloud_logging_configurations, :nodes)).to be_empty
- end
- end
-
- context 'when user is authenticated' do
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when user is instance admin' do
- it 'returns the instance external audit event destinations', :aggregate_failures do
- post_graphql(query, current_user: admin)
-
- expect(graphql_data_at(*path)).to contain_exactly(
- a_hash_including(
- 'googleProjectIdName' => destination_1.google_project_id_name,
- 'clientEmail' => destination_1.client_email,
- 'logIdName' => destination_1.log_id_name,
- 'name' => destination_1.name
- ),
- a_hash_including(
- 'googleProjectIdName' => destination_2.google_project_id_name,
- 'clientEmail' => destination_2.client_email,
- 'logIdName' => destination_2.log_id_name,
- 'name' => destination_2.name
- )
- )
-
- expect(graphql_data_at(*path))
- .to contain_exactly(
- hash_not_including('private_key'),
- hash_not_including('private_key')
- )
- end
- end
-
- context 'when user is not instance admin' do
- it_behaves_like 'a request that returns no destinations' do
- let(:current_user) { user }
- end
- end
- end
-
- context 'when feature is not licensed' do
- context 'when user is instance admin' do
- it_behaves_like 'a request that returns no destinations' do
- let(:current_user) { admin }
- end
- end
-
- context 'when user is not instance admin' do
- it_behaves_like 'a request that returns no destinations' do
- let(:current_user) { user }
- end
- end
- end
- end
-
- context 'when user is not authenticated' do
- let(:user) { nil }
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- it_behaves_like 'a request that returns no destinations' do
- let(:current_user) { user }
- end
- end
-
- context 'when feature is not licensed' do
- it_behaves_like 'a request that returns no destinations' do
- let(:current_user) { user }
- end
- end
- end
-end
diff --git a/ee/spec/requests/api/graphql/audit_events/instance_external_audit_event_destination_spec.rb b/ee/spec/requests/api/graphql/audit_events/instance_external_audit_event_destination_spec.rb
deleted file mode 100644
index 4669e024067b8d337da419d17f29a2c8bebae2fe..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/audit_events/instance_external_audit_event_destination_spec.rb
+++ /dev/null
@@ -1,287 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'getting a list of external audit event destinations for the instance', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:admin) { create(:admin) }
- let_it_be(:user) { create(:user) }
- let_it_be(:destination_1) { create(:instance_external_audit_event_destination) }
- let_it_be(:destination_2) { create(:instance_external_audit_event_destination) }
-
- let(:path) { %i[instance_external_audit_event_destinations nodes] }
-
- let!(:query) do
- graphql_query_for(
- :instance_external_audit_event_destinations
- )
- end
-
- shared_examples 'a request that returns no destinations' do
- it 'returns no destinations' do
- post_graphql(query, current_user: current_user)
-
- expect(graphql_data_at(:instance_external_audit_event_destinations, :nodes)).to be_empty
- end
- end
-
- context 'when user is authenticated' do
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when user is instance admin' do
- it 'returns the instance external audit event destinations' do
- post_graphql(query, current_user: admin)
-
- verification_token_regex = /\A\w{24}\z/i
-
- expect(graphql_data_at(*path)).to contain_exactly(
- a_hash_including(
- 'destinationUrl' => destination_1.destination_url,
- 'verificationToken' => a_string_matching(verification_token_regex)
- ),
- a_hash_including(
- 'destinationUrl' => destination_2.destination_url,
- 'verificationToken' => a_string_matching(verification_token_regex)
- )
- )
- end
-
- context 'when streaming headers are also present for the destination' do
- let_it_be(:header_1) do
- create(:instance_audit_events_streaming_header,
- instance_external_audit_event_destination: destination_1)
- end
-
- let_it_be(:header_2) do
- create(:instance_audit_events_streaming_header,
- instance_external_audit_event_destination: destination_1)
- end
-
- let_it_be(:header_3) do
- create(:instance_audit_events_streaming_header,
- instance_external_audit_event_destination: destination_2)
- end
-
- let_it_be(:query_body) do
- <<~QUERY
- nodes {
- id
- destinationUrl
- headers {
- nodes {
- id
- key
- value
- }
- }
- }
- QUERY
- end
-
- let_it_be(:headers_query) do
- graphql_query_for(
- :instance_external_audit_event_destinations, {}, query_body
- )
- end
-
- it 'returns the instance external audit event destinations with headers' do
- post_graphql(headers_query, current_user: admin)
-
- expected_response = [
- {
- "id" => destination_2.to_gid.to_s,
- "destinationUrl" => destination_2.destination_url,
- "headers" => {
- "nodes" => [
- {
- "id" => header_3.to_gid.to_s,
- "key" => header_3.key,
- "value" => header_3.value
- }
- ]
- }
- },
- {
- "id" => destination_1.to_gid.to_s,
- "destinationUrl" => destination_1.destination_url,
- "headers" => {
- "nodes" => [
- {
- "id" => header_1.to_gid.to_s,
- "key" => header_1.key,
- "value" => header_1.value
- },
- {
- "id" => header_2.to_gid.to_s,
- "key" => header_2.key,
- "value" => header_2.value
- }
- ]
- }
- }
- ]
-
- expect(graphql_data_at(:instance_external_audit_event_destinations, :nodes))
- .to match_array(expected_response)
- end
-
- context 'when streaming event type filters are present for the destination' do
- let_it_be(:filter_1) do
- create(:audit_events_streaming_instance_event_type_filter,
- instance_external_audit_event_destination: destination_1,
- audit_event_type: 'event_type_filters_created')
- end
-
- let_it_be(:filter_2) do
- create(:audit_events_streaming_instance_event_type_filter,
- instance_external_audit_event_destination: destination_1,
- audit_event_type: 'event_type_filters_deleted')
- end
-
- let_it_be(:filter_3) do
- create(:audit_events_streaming_instance_event_type_filter,
- instance_external_audit_event_destination: destination_2,
- audit_event_type: 'audit_events_streaming_headers_create')
- end
-
- let_it_be(:query_body) do
- <<~QUERY
- nodes {
- id
- destinationUrl
- eventTypeFilters
- }
- QUERY
- end
-
- let_it_be(:event_filters_query) do
- graphql_query_for(
- :instance_external_audit_event_destinations, {}, query_body
- )
- end
-
- it 'returns the instance external audit event destinations with event type filters' do
- post_graphql(event_filters_query, current_user: admin)
-
- expected_response = [
- {
- "id" => destination_2.to_gid.to_s,
- "destinationUrl" => destination_2.destination_url,
- "eventTypeFilters" => [filter_3.audit_event_type]
- },
- {
- "id" => destination_1.to_gid.to_s,
- "destinationUrl" => destination_1.destination_url,
- "eventTypeFilters" => [filter_1.audit_event_type, filter_2.audit_event_type]
- }
- ]
-
- expect(graphql_data_at(:instance_external_audit_event_destinations, :nodes))
- .to match_array(expected_response)
- end
- end
- end
-
- context 'when streaming event type filters are present for the destination' do
- let_it_be(:filter_1) do
- create(:audit_events_streaming_instance_event_type_filter,
- instance_external_audit_event_destination: destination_1,
- audit_event_type: 'event_type_filters_created')
- end
-
- let_it_be(:filter_2) do
- create(:audit_events_streaming_instance_event_type_filter,
- instance_external_audit_event_destination: destination_1,
- audit_event_type: 'event_type_filters_deleted')
- end
-
- let_it_be(:filter_3) do
- create(:audit_events_streaming_instance_event_type_filter,
- instance_external_audit_event_destination: destination_2,
- audit_event_type: 'audit_events_streaming_headers_create')
- end
-
- let_it_be(:query_body) do
- <<~QUERY
- nodes {
- id
- destinationUrl
- eventTypeFilters
- }
- QUERY
- end
-
- let_it_be(:event_filters_query) do
- graphql_query_for(
- :instance_external_audit_event_destinations, {}, query_body
- )
- end
-
- it 'returns the instance external audit event destinations with event type filters' do
- post_graphql(event_filters_query, current_user: admin)
-
- expected_response = [
- {
- "id" => destination_2.to_gid.to_s,
- "destinationUrl" => destination_2.destination_url,
- "eventTypeFilters" => [filter_3.audit_event_type]
- },
- {
- "id" => destination_1.to_gid.to_s,
- "destinationUrl" => destination_1.destination_url,
- "eventTypeFilters" => [filter_1.audit_event_type, filter_2.audit_event_type]
- }
- ]
-
- expect(graphql_data_at(:instance_external_audit_event_destinations, :nodes))
- .to match_array(expected_response)
- end
- end
- end
-
- context 'when user is not instance admin' do
- it_behaves_like 'a request that returns no destinations' do
- let(:current_user) { user }
- end
- end
- end
-
- context 'when feature is not licensed' do
- context 'when user is instance admin' do
- it_behaves_like 'a request that returns no destinations' do
- let(:current_user) { admin }
- end
- end
-
- context 'when user is not instance admin' do
- it_behaves_like 'a request that returns no destinations' do
- let(:current_user) { user }
- end
- end
- end
- end
-
- context 'when user is not authenticated' do
- let(:user) { nil }
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- it_behaves_like 'a request that returns no destinations' do
- let(:current_user) { user }
- end
- end
-
- context 'when feature is not licensed' do
- it_behaves_like 'a request that returns no destinations' do
- let(:current_user) { user }
- end
- end
- end
-end
diff --git a/ee/spec/requests/api/graphql/audit_events/streaming/event_type_filters/create_spec.rb b/ee/spec/requests/api/graphql/audit_events/streaming/event_type_filters/create_spec.rb
deleted file mode 100644
index 23df61418b28158447351aae09ff98fddc2fcf3c..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/audit_events/streaming/event_type_filters/create_spec.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Create an audit event type filter', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:destination) { create(:external_audit_event_destination) }
- let_it_be(:event_type_filter) do
- create(:audit_events_streaming_event_type_filter, external_audit_event_destination: destination,
- audit_event_type: 'event_type_filters_created')
- end
-
- let_it_be(:user) { create(:user) }
-
- let_it_be(:current_user) { user }
- let(:group) { destination.group }
- let_it_be(:mutation_name) { :audit_events_streaming_destination_events_add }
- let(:mutation) { graphql_mutation(mutation_name, input) }
- let(:mutation_response) { graphql_mutation_response(mutation_name) }
- let_it_be(:input) { { destinationId: destination.to_gid, eventTypeFilters: ['event_type_filters_deleted'] } }
-
- subject { post_graphql_mutation(mutation, current_user: user) }
-
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is a group maintainer' do
- before do
- group.add_maintainer(user)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- end
-
- context 'when current user is a group developer' do
- before do
- group.add_developer(user)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- end
-
- context 'when current user is a group guest' do
- before do
- group.add_guest(user)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- end
-
- context 'when current user is a group owner' do
- before do
- group.add_owner(user)
- end
-
- include_examples 'create event type filters for external audit event destinations' do
- let_it_be(:non_existing_destination_id) do
- "gid://gitlab/AuditEvents::ExternalAuditEventDestination/#{non_existing_record_id}"
- end
- end
- end
-end
diff --git a/ee/spec/requests/api/graphql/audit_events/streaming/event_type_filters/delete_spec.rb b/ee/spec/requests/api/graphql/audit_events/streaming/event_type_filters/delete_spec.rb
deleted file mode 100644
index fec3b615062cbbedbb1970182b97aa53add8c6f9..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/audit_events/streaming/event_type_filters/delete_spec.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Delete an audit event type filter', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:destination) { create(:external_audit_event_destination) }
- let_it_be(:event_type_filter) do
- create(:audit_events_streaming_event_type_filter, external_audit_event_destination: destination,
- audit_event_type: 'event_type_filters_deleted')
- end
-
- let_it_be(:user) { create(:user) }
-
- let(:current_user) { user }
- let(:group) { destination.group }
- let(:mutation_name) { :audit_events_streaming_destination_events_remove }
- let(:mutation) { graphql_mutation(mutation_name, input) }
- let(:mutation_response) { graphql_mutation_response(mutation_name) }
- let(:input) { { destinationId: destination.to_gid, eventTypeFilters: ['event_type_filters_deleted'] } }
-
- context 'when unlicensed' do
- subject { post_graphql_mutation(mutation, current_user: user) }
-
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- end
-
- context 'when licensed' do
- subject(:mutate) { post_graphql_mutation(mutation, current_user: user) }
-
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is a group maintainer' do
- before do
- group.add_maintainer(user)
- end
-
- it_behaves_like 'a mutation that returns top-level errors',
- errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
- end
-
- context 'when current user is a group developer' do
- before do
- group.add_developer(user)
- end
-
- it_behaves_like 'a mutation that returns top-level errors',
- errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
- end
-
- context 'when current user is a group guest' do
- before do
- group.add_guest(user)
- end
-
- it_behaves_like 'a mutation that returns top-level errors',
- errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
- end
-
- context 'when current user is a group owner' do
- before do
- group.add_owner(user)
- end
-
- it 'returns success response' do
- mutate
-
- expect(mutation_response["errors"]).to be_empty
- end
-
- context 'when event type filters in input is empty' do
- let(:input) { { destinationId: destination.to_gid, eventTypeFilters: [] } }
-
- it 'returns graphql error' do
- mutate
-
- expect(graphql_errors).to include(a_hash_including('message' => 'event type filters must be present'))
- end
- end
- end
- end
-end
diff --git a/ee/spec/requests/api/graphql/audit_events/streaming/headers/create_spec.rb b/ee/spec/requests/api/graphql/audit_events/streaming/headers/create_spec.rb
deleted file mode 100644
index f208f33457df68066257e2fb320b46d55babd279..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/audit_events/streaming/headers/create_spec.rb
+++ /dev/null
@@ -1,125 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Create an external audit event destination header', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:destination) { create(:external_audit_event_destination) }
- let_it_be(:owner) { create(:user) }
-
- let(:current_user) { owner }
- let(:group) { destination.group }
- let(:mutation) { graphql_mutation(:audit_events_streaming_headers_create, input) }
- let(:mutation_response) { graphql_mutation_response(:audit_events_streaming_headers_create) }
-
- let(:input) do
- {
- destinationId: destination.to_gid,
- key: 'foo',
- value: 'bar'
- }
- end
-
- let(:invalid_input) do
- {
- destinationId: destination.to_gid,
- key: '',
- value: 'bar'
- }
- end
-
- shared_examples 'a mutation that does not create a header' do
- it 'does not create a header' do
- expect { post_graphql_mutation(mutation, current_user: owner) }
- .not_to change { destination.headers.count }
- end
- end
-
- context 'when feature is licensed' do
- subject { post_graphql_mutation(mutation, current_user: owner) }
-
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is a group owner' do
- before do
- group.add_owner(owner)
- end
-
- it 'creates the header with the correct attributes', :aggregate_failures do
- expect { subject }
- .to change { destination.headers.count }.by(1)
-
- header = AuditEvents::Streaming::Header.last
-
- expect(header.key).to eq('foo')
- expect(header.value).to eq('bar')
- expect(header.active).to eq(true)
- end
-
- context 'when active param is also provided' do
- let(:input) { super().merge(active: false) }
-
- it 'creates the header with the correct attributes', :aggregate_failures do
- expect { subject }
- .to change { destination.headers.count }.by(1)
-
- header = AuditEvents::Streaming::Header.last
-
- expect(header.key).to eq('foo')
- expect(header.value).to eq('bar')
- expect(header.active).to eq(false)
- expect(mutation_response['errors']).to be_empty
- end
- end
-
- context 'when the header attributes are invalid' do
- let(:mutation) { graphql_mutation(:audit_events_streaming_headers_create, invalid_input) }
-
- it 'returns correct errors' do
- post_graphql_mutation(mutation, current_user: owner)
-
- expect(mutation_response['header']).to be_nil
- expect(mutation_response['errors']).to contain_exactly("Key can't be blank")
- end
-
- it_behaves_like 'a mutation that does not create a header'
- end
- end
-
- context 'when current user is a group maintainer' do
- before do
- group.add_maintainer(owner)
- end
-
- it_behaves_like 'a mutation that does not create a header'
- end
-
- context 'when current user is a group developer' do
- before do
- group.add_developer(owner)
- end
-
- it_behaves_like 'a mutation that does not create a header'
- end
-
- context 'when current user is a group guest' do
- before do
- group.add_guest(owner)
- end
-
- it_behaves_like 'a mutation that does not create a header'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not create a header'
- end
-end
diff --git a/ee/spec/requests/api/graphql/audit_events/streaming/headers/destroy_spec.rb b/ee/spec/requests/api/graphql/audit_events/streaming/headers/destroy_spec.rb
deleted file mode 100644
index 8e51b9e7dfe13d979d08837539c05e879e17d1ba..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/audit_events/streaming/headers/destroy_spec.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Destroy an external audit event destination header', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:destination) { create(:external_audit_event_destination) }
- let_it_be(:owner) { create(:user) }
- let_it_be(:header) { create(:audit_events_streaming_header, external_audit_event_destination: destination) }
-
- let(:current_user) { owner }
- let(:group) { destination.group }
- let(:mutation) { graphql_mutation(:audit_events_streaming_headers_destroy, input) }
- let(:mutation_response) { graphql_mutation_response(:audit_events_streaming_headers_destroy) }
-
- let(:input) do
- { headerId: header.to_gid }
- end
-
- shared_examples 'a mutation that does not destroy a header' do
- it 'does not destroy the destination' do
- expect { post_graphql_mutation(mutation, current_user: owner) }
- .not_to change { destination.headers.count }
- end
- end
-
- context 'when feature is licensed' do
- subject { post_graphql_mutation(mutation, current_user: owner) }
-
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is a group owner' do
- before do
- group.add_owner(owner)
- end
-
- it 'destroys the header' do
- expect { subject }
- .to change { destination.headers.count }.by(-1)
- end
-
- context 'when header ID belongs to a different destination' do
- let_it_be(:header) { create(:audit_events_streaming_header) }
-
- it_behaves_like 'a mutation that does not destroy a header'
- end
- end
-
- context 'when current user is a group maintainer' do
- before do
- group.add_maintainer(owner)
- end
-
- it_behaves_like 'a mutation that does not destroy a header'
- end
-
- context 'when current user is a group developer' do
- before do
- group.add_developer(owner)
- end
-
- it_behaves_like 'a mutation that does not destroy a header'
- end
-
- context 'when current user is a group guest' do
- before do
- group.add_guest(owner)
- end
-
- it_behaves_like 'a mutation that does not destroy a header'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
-
- it 'does not destroy the header' do
- expect { post_graphql_mutation(mutation, current_user: owner) }
- .not_to change { destination.headers.count }
- end
- end
-end
diff --git a/ee/spec/requests/api/graphql/audit_events/streaming/headers/update_spec.rb b/ee/spec/requests/api/graphql/audit_events/streaming/headers/update_spec.rb
deleted file mode 100644
index 6529a898df4ed304c93dee0a4aa9c32e35307554..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/audit_events/streaming/headers/update_spec.rb
+++ /dev/null
@@ -1,151 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Update an external audit event destination header', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:destination) { create(:external_audit_event_destination) }
- let_it_be_with_reload(:header) do
- create(:audit_events_streaming_header, key: 'key-1', external_audit_event_destination: destination)
- end
-
- let_it_be(:owner) { create(:user) }
-
- let(:current_user) { owner }
- let(:group) { destination.group }
- let(:mutation) { graphql_mutation(:audit_events_streaming_headers_update, input) }
- let(:mutation_response) { graphql_mutation_response(:audit_events_streaming_headers_update) }
-
- let(:input) do
- {
- headerId: header.to_gid,
- key: 'new-key',
- value: 'new-value'
- }
- end
-
- let(:invalid_input) do
- {
- headerId: header.to_gid,
- key: '',
- value: 'bar'
- }
- end
-
- shared_examples 'a mutation that does not update a header' do
- it 'does not update a header key' do
- expect { post_graphql_mutation(mutation, current_user: owner) }.not_to change { header.key }
- end
-
- it 'does not update a header value' do
- expect { post_graphql_mutation(mutation, current_user: owner) }.not_to change { header.value }
- end
-
- it 'does not create any audit event', :aggregate_failures do
- expect(::Gitlab::Audit::Auditor).not_to receive(:audit)
-
- expect { post_graphql_mutation(mutation, current_user: owner) }.not_to change { AuditEvent.count }
- end
- end
-
- context 'when feature is licensed' do
- subject { post_graphql_mutation(mutation, current_user: owner) }
-
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is a group owner' do
- before do
- group.add_owner(owner)
- end
-
- it 'updates the header with the correct attributes', :aggregate_failures do
- expect { subject }
- .to change { header.reload.key }.from('key-1').to('new-key')
- .and change { header.reload.value }.from('bar').to('new-value')
- .and not_change { header.reload.active }
- end
-
- context 'when active attribute is also updated' do
- let(:input) { super().merge(active: false) }
-
- it 'updates the header with the correct attributes', :aggregate_failures do
- expect { subject }.to change { header.reload.key }.from('key-1').to('new-key')
- .and change { header.reload.value }
- .from('bar').to('new-value')
- .and change { header.reload.active }.from(true).to(false)
- end
- end
-
- context 'when only active attribute is updated' do
- let(:input) do
- {
- headerId: header.to_gid,
- active: false
- }
- end
-
- it 'updates the header active value' do
- expect { subject }
- .to change { header.reload.active }.from(true).to(false)
- .and not_change { header.reload.key }
- .and not_change { header.reload.value }
- end
- end
-
- context 'when the header attributes are invalid' do
- let(:mutation) { graphql_mutation(:audit_events_streaming_headers_update, invalid_input) }
-
- it 'returns correct errors' do
- post_graphql_mutation(mutation, current_user: owner)
-
- expect(mutation_response['errors']).to contain_exactly("Key can't be blank")
- end
-
- it 'returns the unmutated attribute values', :aggregate_failures do
- post_graphql_mutation(mutation, current_user: owner)
-
- expect(mutation_response.dig('header', 'key')).to eq('key-1')
- expect(mutation_response.dig('header', 'value')).to eq('bar')
- end
-
- it_behaves_like 'a mutation that does not update a header'
- end
- end
-
- context 'when current user is a group maintainer' do
- before do
- group.add_maintainer(owner)
- end
-
- it_behaves_like 'a mutation that does not update a header'
- end
-
- context 'when current user is a group developer' do
- before do
- group.add_developer(owner)
- end
-
- it_behaves_like 'a mutation that does not update a header'
- end
-
- context 'when current user is a group guest' do
- before do
- group.add_guest(owner)
- end
-
- it_behaves_like 'a mutation that does not update a header'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not update a header'
- end
-end
diff --git a/ee/spec/requests/api/graphql/audit_events/streaming/http/namespace_filters/create_spec.rb b/ee/spec/requests/api/graphql/audit_events/streaming/http/namespace_filters/create_spec.rb
deleted file mode 100644
index fd3da49dcaf8b944eb5015205b7a861ae3323d2d..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/audit_events/streaming/http/namespace_filters/create_spec.rb
+++ /dev/null
@@ -1,289 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Create a namespace filter for group level external audit event destinations', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:group) { create(:group) }
- let(:destination) { create(:external_audit_event_destination, group: group) }
- let_it_be(:current_user) { create(:user) }
- let(:mutation) { graphql_mutation(:audit_events_streaming_http_namespace_filters_add, input) }
- let(:mutation_response) { graphql_mutation_response(:audit_events_streaming_http_namespace_filters_add) }
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: current_user) }
-
- shared_examples 'does not create namespace filter' do
- it do
- expect(::Gitlab::Audit::Auditor).not_to receive(:audit)
-
- expect { mutate }.not_to change { AuditEvents::Streaming::HTTP::NamespaceFilter.count }
-
- expect(graphql_errors).to include(a_hash_including('message' => error_message))
- expect(mutation_response).to eq(nil)
- end
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is a group owner' do
- before_all do
- group.add_owner(current_user)
- end
-
- shared_examples 'creation of namespace filters with one path' do
- context 'when namespace is a descendant of the destination group' do
- let(:input) do
- {
- destinationId: destination.to_gid,
- "#{namespace_path.camelize(:lower)}": namespace.full_path
- }
- end
-
- it 'creates a namespace filter', :aggregate_failures do
- expect(::Gitlab::Audit::Auditor).to receive(:audit).with(a_hash_including(
- name: 'create_http_namespace_filter',
- author: current_user,
- scope: group,
- target: destination,
- message: "Create namespace filter for http audit event streaming destination #{destination.name} " \
- "and namespace #{namespace.full_path}")).once.and_call_original
-
- expect { mutate }
- .to change { AuditEvent.count }.by(1)
-
- namespace_filter = destination.namespace_filter
- expect(namespace_filter.namespace).to eq(namespace)
- expect(namespace_filter.external_audit_event_destination).to eq(destination)
-
- expect_graphql_errors_to_be_empty
-
- expect(mutation_response['errors']).to be_empty
- expect(mutation_response).to have_key('namespaceFilter')
- expect(mutation_response['namespaceFilter']['namespace']['fullPath']).to eq(namespace.full_path)
- expect(mutation_response['namespaceFilter']['externalAuditEventDestination']['name'])
- .to eq(destination.name)
- end
-
- context 'when namespace filter for the destination already exists' do
- before do
- create(:audit_events_streaming_http_namespace_filter, external_audit_event_destination: destination,
- namespace: create(:group, parent: group))
- end
-
- it 'returns error' do
- expect { mutate }.not_to change { AuditEvents::Streaming::HTTP::NamespaceFilter.count }
-
- expect(mutation_response['errors'])
- .to match_array(['External audit event destination has already been taken'])
- expect(mutation_response['namespaceFilter']).to be_nil
- end
- end
-
- context 'when namespace filter for the given namespace already exists' do
- before do
- create(:audit_events_streaming_http_namespace_filter,
- external_audit_event_destination: create(:external_audit_event_destination, group: group),
- namespace: namespace
- )
- end
-
- it 'returns error' do
- expect { mutate }.not_to change { AuditEvents::Streaming::HTTP::NamespaceFilter.count }
-
- expect(mutation_response['errors']).to match_array(['Namespace has already been taken'])
- expect(mutation_response['namespaceFilter']).to be_nil
- end
- end
- end
-
- context 'when namespace group is not a descendant of the destination group' do
- let(:input) do
- {
- destinationId: destination.to_gid,
- "#{namespace_path.camelize(:lower)}": other_namespace.full_path
- }
- end
-
- it 'returns error' do
- expect(::Gitlab::Audit::Auditor).not_to receive(:audit)
-
- expect { mutate }.not_to change { AuditEvents::Streaming::HTTP::NamespaceFilter.count }
-
- expect(mutation_response).to include(
- 'errors' => ['External audit event destination does not belong to the top-level group of the namespace.']
- )
- expect(mutation_response['namespaceFilter']).to eq(nil)
- end
- end
-
- context 'when given namespace path is invalid' do
- let(:input) do
- {
- destinationId: destination.to_gid,
- "#{namespace_path.camelize(:lower)}": 'invalid_path'
- }
- end
-
- it 'returns error' do
- expect(::Gitlab::Audit::Auditor).not_to receive(:audit)
-
- expect { mutate }.not_to change { AuditEvents::Streaming::HTTP::NamespaceFilter.count }
-
- expect(graphql_errors).to include(a_hash_including('message' => "#{namespace_path} is invalid"))
- expect(mutation_response).to eq(nil)
- end
- end
- end
-
- context 'when group_path is passed in params' do
- it_behaves_like 'creation of namespace filters with one path' do
- let_it_be(:namespace) { create(:group, parent: group) }
- let_it_be(:other_namespace) { create(:group) }
- let(:namespace_path) { "group_path" }
- end
- end
-
- context 'when project_path is passed in params' do
- it_behaves_like 'creation of namespace filters with one path' do
- let_it_be(:project) { create(:project, group: group) }
- let_it_be(:namespace) { project.project_namespace }
- let_it_be(:other_namespace) { create(:project_namespace) }
- let(:namespace_path) { "project_path" }
- end
- end
-
- context 'when both group_path and project_path are passed in params' do
- let_it_be(:namespace_group) { create(:group, parent: group) }
- let_it_be(:namespace_project) { create(:project, group: group) }
-
- let(:input) do
- {
- destinationId: destination.to_gid,
- projectPath: namespace_project.full_path,
- groupPath: namespace_group.full_path
- }
- end
-
- let(:error_message) { 'One and only one of [groupPath, projectPath] arguments is required.' }
-
- it_behaves_like 'does not create namespace filter'
- end
-
- context 'when none of group_path and project_path is passed in params' do
- let(:input) do
- {
- destinationId: destination.to_gid
- }
- end
-
- let(:error_message) { 'One and only one of [groupPath, projectPath] arguments is required.' }
-
- it_behaves_like 'does not create namespace filter'
- end
-
- context 'with sync functionality' do
- let(:namespace_path) { "group_path" }
- let_it_be(:namespace) { create(:group, parent: group) }
-
- let(:stream_destination) { create(:audit_events_group_external_streaming_destination, group: group) }
- let(:input) do
- {
- destinationId: destination.to_gid,
- "#{namespace_path.camelize(:lower)}": namespace.full_path
- }
- end
-
- context 'when legacy destination has corresponding streaming destination' do
- before do
- destination.update_column(:stream_destination_id, stream_destination.id)
- end
-
- it 'calls sync method after successful operation' do
- allow_next_instance_of(Mutations::AuditEvents::Streaming::HTTP::NamespaceFilters::Create) do |instance|
- allow(instance).to receive(:sync_stream_namespace_filter).and_return(nil)
- end
-
- mutate
- expect_graphql_errors_to_be_empty
- end
- end
- end
- end
-
- context 'when current user is a group maintainer' do
- before_all do
- group.add_maintainer(current_user)
- end
-
- let_it_be(:namespace_group) { create(:group, parent: group) }
- let(:input) do
- {
- destinationId: destination.to_gid,
- groupPath: namespace_group.full_path
- }
- end
-
- let(:error_message) { ::Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR }
-
- it_behaves_like 'does not create namespace filter'
- end
-
- context 'when current user is a group developer' do
- before_all do
- group.add_developer(current_user)
- end
-
- let_it_be(:namespace_group) { create(:group, parent: group) }
- let(:input) do
- {
- destinationId: destination.to_gid,
- groupPath: namespace_group.full_path
- }
- end
-
- let(:error_message) { ::Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR }
-
- it_behaves_like 'does not create namespace filter'
- end
-
- context 'when current user is a group guest' do
- before_all do
- group.add_guest(current_user)
- end
-
- let_it_be(:namespace_group) { create(:group, parent: group) }
- let(:input) do
- {
- destinationId: destination.to_gid,
- groupPath: namespace_group.full_path
- }
- end
-
- let(:error_message) { ::Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR }
-
- it_behaves_like 'does not create namespace filter'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- let_it_be(:namespace_group) { create(:group, parent: group) }
- let(:input) do
- {
- destinationId: destination.to_gid,
- groupPath: namespace_group.full_path
- }
- end
-
- let(:error_message) { ::Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR }
-
- it_behaves_like 'does not create namespace filter'
- end
-end
diff --git a/ee/spec/requests/api/graphql/audit_events/streaming/http/namespace_filters/delete_spec.rb b/ee/spec/requests/api/graphql/audit_events/streaming/http/namespace_filters/delete_spec.rb
deleted file mode 100644
index 161fbe5de6b22df03417ae026c7bd7864a68e513..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/audit_events/streaming/http/namespace_filters/delete_spec.rb
+++ /dev/null
@@ -1,120 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Delete a namespace filter for group level external audit event destinations', feature_category: :audit_events do
- include GraphqlHelpers
-
- let(:current_user) { create(:user) }
- let(:group) { create(:group) }
- let(:subgroup) { create(:group, parent: group) }
- let(:destination) { create(:external_audit_event_destination, group: group) }
- let!(:filter) do
- create(:audit_events_streaming_http_namespace_filter, external_audit_event_destination: destination,
- namespace: subgroup)
- end
-
- let(:mutation) { graphql_mutation(:audit_events_streaming_http_namespace_filters_delete, input) }
- let(:mutation_response) { graphql_mutation_response(:audit_events_streaming_http_namespace_filters_delete) }
-
- let(:input) do
- { namespaceFilterId: filter.to_gid }
- end
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: current_user) }
-
- shared_examples 'does not delete the namespace filter' do
- it do
- expect(::Gitlab::Audit::Auditor).not_to receive(:audit)
- .with(a_hash_including(name: 'delete_http_namespace_filter'))
-
- expect { subject }.not_to change { destination.reload.namespace_filter }
- end
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is group owner' do
- before do
- group.add_owner(current_user)
- end
-
- it 'deletes the filter', :aggregate_failures do
- expect(::Gitlab::Audit::Auditor).to receive(:audit).with(a_hash_including(
- name: 'delete_http_namespace_filter',
- author: current_user,
- scope: group,
- target: destination,
- message: "Delete namespace filter for http audit event streaming destination #{destination.name} " \
- "and namespace #{subgroup.full_path}")).once.and_call_original
-
- expect { mutate }.to change { AuditEvents::Streaming::HTTP::NamespaceFilter.count }.by(-1)
-
- expect(destination.reload.namespace_filter).to be_nil
- expect_graphql_errors_to_be_empty
- expect(mutation_response['errors']).to be_empty
- expect(mutation_response['namespaceFilter']).to be_nil
- end
-
- context 'with sync functionality' do
- let(:input) do
- { namespaceFilterId: filter.to_gid }
- end
-
- let(:stream_destination) { create(:audit_events_group_external_streaming_destination, group: group) }
-
- context 'when legacy destination has corresponding streaming destination' do
- before do
- destination.update_column(:stream_destination_id, stream_destination.id)
- end
-
- it 'calls sync method after successful deletion' do
- allow_next_instance_of(Mutations::AuditEvents::Streaming::HTTP::NamespaceFilters::Delete) do |instance|
- allow(instance).to receive(:sync_delete_stream_namespace_filter).and_return(nil)
- end
-
- mutate
-
- expect_graphql_errors_to_be_empty
- expect(mutation_response['errors']).to be_empty
- expect(mutation_response['namespaceFilter']).to be_nil
- end
- end
-
- context 'when legacy destination has no streaming destination' do
- it 'still calls sync method but it does nothing' do
- allow_next_instance_of(Mutations::AuditEvents::Streaming::HTTP::NamespaceFilters::Delete) do |instance|
- allow(instance).to receive(:sync_delete_stream_namespace_filter).and_return(nil)
- end
-
- mutate
-
- expect_graphql_errors_to_be_empty
- expect(mutation_response['errors']).to be_empty
- end
- end
- end
- end
-
- context 'when current user is a group maintainer' do
- before do
- group.add_maintainer(current_user)
- end
-
- it_behaves_like 'does not delete the namespace filter'
- end
- end
-
- context 'when feature is not licensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
-
- it_behaves_like 'does not delete the namespace filter'
- end
-end
diff --git a/ee/spec/requests/api/graphql/audit_events/streaming/instance_event_type_filters/create_spec.rb b/ee/spec/requests/api/graphql/audit_events/streaming/instance_event_type_filters/create_spec.rb
deleted file mode 100644
index d30b058b8fe3e184e8063562753b13d44ab1b197..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/audit_events/streaming/instance_event_type_filters/create_spec.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Create an instance audit event type filter', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:destination) { create(:instance_external_audit_event_destination) }
- let_it_be(:event_type_filter) do
- create(:audit_events_streaming_instance_event_type_filter, instance_external_audit_event_destination: destination,
- audit_event_type: 'event_type_filters_created')
- end
-
- let_it_be(:mutation_name) { :audit_events_streaming_destination_instance_events_add }
- let(:mutation) { graphql_mutation(mutation_name, input) }
- let(:mutation_response) { graphql_mutation_response(mutation_name) }
- let_it_be(:input) { { destinationId: destination.to_gid, eventTypeFilters: ['event_type_filters_deleted'] } }
-
- subject { post_graphql_mutation(mutation, current_user: current_user) }
-
- context 'when current user is instance admin' do
- let(:current_user) { create(:admin) }
-
- include_examples 'create event type filters for external audit event destinations' do
- let_it_be(:non_existing_destination_id) do
- "gid://gitlab/AuditEvents::InstanceExternalAuditEventDestination/#{non_existing_record_id}"
- end
- end
- end
-
- context 'when current user is not instance admin' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- let_it_be(:current_user) { create(:user) }
-
- it_behaves_like 'a mutation on an unauthorized resource'
- end
-end
diff --git a/ee/spec/requests/api/graphql/audit_events/streaming/instance_event_type_filters/destroy_spec.rb b/ee/spec/requests/api/graphql/audit_events/streaming/instance_event_type_filters/destroy_spec.rb
deleted file mode 100644
index 3a39dd20023ff7189c2e73b173c65879dcc13b24..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/audit_events/streaming/instance_event_type_filters/destroy_spec.rb
+++ /dev/null
@@ -1,124 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Delete an instance level audit event type filter', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be_with_reload(:destination) { create(:instance_external_audit_event_destination) }
-
- let(:mutation_name) { :audit_events_streaming_destination_instance_events_remove }
- let(:mutation) { graphql_mutation(mutation_name, input) }
- let(:mutation_response) { graphql_mutation_response(mutation_name) }
- let(:input) { { destinationId: destination.to_gid, eventTypeFilters: ['event_type_filters_created'] } }
-
- before_all do
- create(:audit_events_streaming_instance_event_type_filter, instance_external_audit_event_destination: destination,
- audit_event_type: 'event_type_filters_created')
- create(:audit_events_streaming_instance_event_type_filter, instance_external_audit_event_destination: destination,
- audit_event_type: 'event_type_filters_deleted')
- end
-
- context 'when current user is instance admin' do
- let(:current_user) { create(:admin) }
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: current_user) }
-
- context 'when licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- shared_examples 'deletes event filter' do
- it do
- expect { mutate }.to change { destination.event_type_filters.count }.by(-1)
-
- expect(mutation_response["errors"]).to be_empty
- end
- end
-
- context 'when all params are correct' do
- it_behaves_like 'deletes event filter'
- end
-
- context 'when destination id is not in input params' do
- let(:input) { { eventTypeFilters: ['event_type_filters_created'] } }
-
- it 'returns error', :aggregate_failures do
- expect { mutate }.not_to change { AuditEvents::Streaming::InstanceEventTypeFilter.count }
-
- expect(graphql_errors.to_s).to include("invalid value for destinationId (Expected value to not be null")
- end
- end
-
- context 'when destination id is not existing' do
- let(:input) do
- {
- destinationId: "gid://gitlab/AuditEvents::InstanceExternalAuditEventDestination/#{non_existing_record_id}",
- eventTypeFilters: ['event_type_filters_created']
- }
- end
-
- it 'does not delete any event filter' do
- expect { mutate }.not_to change { AuditEvents::Streaming::InstanceEventTypeFilter.count }
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- end
-
- context 'when event filters is not in input params' do
- let(:input) { { destinationId: destination.to_gid } }
-
- it 'returns error', :aggregate_failures do
- expect { mutate }.not_to change { destination.event_type_filters.count }
-
- expect(graphql_errors.to_s).to include("invalid value for eventTypeFilters (Expected value to not be null")
- end
- end
-
- context 'when event filters is not an array' do
- let(:input) { { destinationId: destination.to_gid, eventTypeFilters: 'event_type_filters_created' } }
-
- it_behaves_like 'deletes event filter'
- end
-
- context 'when the given event filters does not exist for the destination' do
- let(:input) do
- { destinationId: destination.to_gid, eventTypeFilters: ['audit_events_streaming_headers_create'] }
- end
-
- it 'returns error', :aggregate_failures do
- expect { mutate }.not_to change { destination.event_type_filters.count }
-
- expect(mutation_response["errors"])
- .to eq(["Couldn't find event type filters where audit event type(s): " \
- "audit_events_streaming_headers_create"])
- end
- end
-
- context 'when event type filters in input is empty' do
- let(:input) { { destinationId: destination.to_gid, eventTypeFilters: [] } }
-
- it 'returns graphql error' do
- expect { mutate }.not_to change { AuditEvents::Streaming::InstanceEventTypeFilter.count }
-
- expect(graphql_errors).to include(a_hash_including('message' => 'event type filters must be present'))
- end
- end
- end
-
- context 'when unlicensed' do
- it_behaves_like 'a mutation on an unauthorized resource'
- end
- end
-
- context 'when current user is not instance admin' do
- let(:current_user) { create(:user) }
-
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- end
-end
diff --git a/ee/spec/requests/api/graphql/audit_events/streaming/instance_headers/create_spec.rb b/ee/spec/requests/api/graphql/audit_events/streaming/instance_headers/create_spec.rb
deleted file mode 100644
index 2a3c1a23ccc88613e4b19215f6421e43b30d000a..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/audit_events/streaming/instance_headers/create_spec.rb
+++ /dev/null
@@ -1,135 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Create an instance external audit event destination header', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:destination) { create(:instance_external_audit_event_destination) }
- let_it_be(:admin) { create(:admin) }
- let_it_be(:user) { create(:user) }
-
- let_it_be(:current_user) { admin }
-
- let(:mutation) { graphql_mutation(:audit_events_streaming_instance_headers_create, input) }
- let(:mutation_response) { graphql_mutation_response(:audit_events_streaming_instance_headers_create) }
-
- let(:input) do
- {
- destinationId: destination.to_gid,
- key: 'foo',
- value: 'bar'
- }
- end
-
- subject { post_graphql_mutation(mutation, current_user: current_user) }
-
- before do
- allow(Gitlab::Audit::Type::Definition).to receive(:defined?).and_return(true)
- end
-
- shared_examples 'a mutation that does not create a header' do
- it 'does not create a header' do
- expect { post_graphql_mutation(mutation, current_user: current_user) }
- .not_to change { destination.headers.count }
- end
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is instance admin' do
- it 'creates the header with the correct attributes', :aggregate_failures do
- expect { subject }
- .to change { destination.headers.count }.by(1)
-
- header = AuditEvents::Streaming::InstanceHeader.last
-
- expect(header.key).to eq('foo')
- expect(header.value).to eq('bar')
- expect(header.active).to eq(true)
- expect(mutation_response['errors']).to be_empty
- end
-
- context 'when active param is also provided' do
- let(:input) { super().merge(active: false) }
-
- it 'creates the header with the correct attributes', :aggregate_failures do
- expect { subject }
- .to change { destination.headers.count }.by(1)
-
- header = AuditEvents::Streaming::InstanceHeader.last
-
- expect(header.key).to eq('foo')
- expect(header.value).to eq('bar')
- expect(header.active).to eq(false)
- expect(mutation_response['errors']).to be_empty
- end
- end
-
- context 'when the header attributes are invalid' do
- let_it_be(:invalid_headers_input) do
- {
- destinationId: destination.to_gid,
- key: '',
- value: 'bar'
- }
- end
-
- let(:mutation) { graphql_mutation(:audit_events_streaming_instance_headers_create, invalid_headers_input) }
-
- it 'returns correct errors' do
- subject
-
- expect(mutation_response['header']).to be_nil
- expect(mutation_response['errors']).to contain_exactly("Key can't be blank")
- end
-
- it_behaves_like 'a mutation that does not create a header' do
- let_it_be(:current_user) { admin }
- end
- end
-
- context 'when the destination id is wrong' do
- let_it_be(:invalid_destination_input) do
- {
- destinationId: "gid://gitlab/AuditEvents::InstanceExternalAuditEventDestination/14566",
- key: 'foo',
- value: 'bar'
- }
- end
-
- let(:mutation) do
- graphql_mutation(:audit_events_streaming_instance_headers_create, invalid_destination_input)
- end
-
- it_behaves_like 'a mutation that returns top-level errors',
- errors: [Mutations::AuditEvents::Streaming::InstanceHeaders::Base::DESTINATION_ERROR_MESSAGE]
-
- it 'does not create any header' do
- expect { post_graphql_mutation(mutation, current_user: current_user) }
- .not_to change { AuditEvents::Streaming::InstanceHeader.count }
- end
- end
- end
-
- context 'when current user is not instance admin' do
- it_behaves_like 'a mutation that does not create a header' do
- let_it_be(:current_user) { user }
- end
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation that returns top-level errors',
- errors: [Mutations::AuditEvents::Streaming::InstanceHeaders::Base::ERROR_MESSAGE]
-
- it_behaves_like 'a mutation that does not create a header'
- end
-end
diff --git a/ee/spec/requests/api/graphql/audit_events/streaming/instance_headers/destroy_spec.rb b/ee/spec/requests/api/graphql/audit_events/streaming/instance_headers/destroy_spec.rb
deleted file mode 100644
index 6e94f1e47bda9bcbf21087f73a9c8892c6e3193c..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/audit_events/streaming/instance_headers/destroy_spec.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Destroy an external audit event destination header', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:destination) { create(:instance_external_audit_event_destination) }
- let_it_be(:header) do
- create(:instance_audit_events_streaming_header, instance_external_audit_event_destination: destination)
- end
-
- let_it_be(:admin) { create(:admin) }
- let_it_be(:user) { create(:user) }
-
- let(:current_user) { admin }
- let(:mutation) { graphql_mutation(:audit_events_streaming_instance_headers_destroy, input) }
- let(:mutation_response) { graphql_mutation_response(:audit_events_streaming_instance_headers_destroy) }
-
- let(:input) do
- {
- headerId: header.to_gid
- }
- end
-
- subject { post_graphql_mutation(mutation, current_user: current_user) }
-
- shared_examples 'a mutation that does not destroy a header' do
- it 'does not destroy the destination' do
- expect { post_graphql_mutation(mutation, current_user: actioner) }
- .not_to change { destination.headers.count }
- end
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is instance admin' do
- it 'destroys the header' do
- expect { subject }
- .to change { destination.headers.count }.by(-1)
- end
-
- context 'when the header id is wrong' do
- let_it_be(:invalid_header_input) do
- {
- headerId: "gid://gitlab/AuditEvents::Streaming::InstanceHeader/#{non_existing_record_id}"
- }
- end
-
- let(:mutation) { graphql_mutation(:audit_events_streaming_instance_headers_destroy, invalid_header_input) }
-
- it_behaves_like 'a mutation that returns top-level errors',
- errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
-
- it_behaves_like 'a mutation that does not destroy a header' do
- let_it_be(:actioner) { admin }
- end
- end
-
- context 'when there is an error while deleting header' do
- before do
- allow_next_found_instance_of(AuditEvents::Streaming::InstanceHeader) do |instance|
- allow(instance).to receive(:destroy).and_return(false)
- allow(instance).to receive(:errors).and_return('foo_error')
- end
- end
-
- it 'returns correct error' do
- expect { subject }.to not_change { destination.headers.count }
-
- expect(mutation_response['errors']).to contain_exactly("foo_error")
- end
- end
- end
-
- context 'when current user is not instance admin' do
- it_behaves_like 'a mutation that does not destroy a header' do
- let_it_be(:actioner) { user }
- end
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation that returns top-level errors',
- errors: [Mutations::AuditEvents::Streaming::InstanceHeaders::Base::ERROR_MESSAGE]
-
- it_behaves_like 'a mutation that does not destroy a header' do
- let_it_be(:actioner) { admin }
- end
- end
-end
diff --git a/ee/spec/requests/api/graphql/audit_events/streaming/instance_headers/update_spec.rb b/ee/spec/requests/api/graphql/audit_events/streaming/instance_headers/update_spec.rb
deleted file mode 100644
index 900f4b5724f4b6e22e2a7c8d293a6998e4799a62..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/audit_events/streaming/instance_headers/update_spec.rb
+++ /dev/null
@@ -1,151 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Update an external audit event destination header', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:destination) { create(:instance_external_audit_event_destination) }
- let_it_be(:header) do
- create(:instance_audit_events_streaming_header,
- key: 'key-1',
- instance_external_audit_event_destination: destination
- )
- end
-
- let_it_be(:admin) { create(:admin) }
- let_it_be(:user) { create(:user) }
-
- let(:current_user) { admin }
- let(:mutation) { graphql_mutation(:audit_events_streaming_instance_headers_update, input) }
- let(:mutation_response) { graphql_mutation_response(:audit_events_streaming_instance_headers_update) }
-
- let(:input) do
- {
- headerId: header.to_gid,
- key: 'new-key',
- value: 'new-value'
- }
- end
-
- subject { post_graphql_mutation(mutation, current_user: current_user) }
-
- shared_examples 'a mutation that does not update a header' do
- it 'does not update a header key' do
- expect { post_graphql_mutation(mutation, current_user: actioner) }.not_to change { header.key }
- end
-
- it 'does not update a header value' do
- expect { post_graphql_mutation(mutation, current_user: actioner) }.not_to change { header.value }
- end
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is instance admin' do
- it 'updates the header with the correct attributes', :aggregate_failures do
- expect { subject }
- .to change { header.reload.key }.from('key-1').to('new-key')
- .and change { header.value }.from('bar').to('new-value')
- .and not_change { header.active }
- end
-
- context 'when active attribute is also updated' do
- let(:input) { super().merge(active: false) }
-
- it 'updates the header with the correct attributes', :aggregate_failures do
- expect { subject }.to change { header.reload.key }.from('key-1').to('new-key')
- .and change { header.reload.value }
- .from('bar').to('new-value')
- .and change { header.reload.active }.from(true).to(false)
- end
- end
-
- context 'when only active attribute is updated' do
- let(:input) do
- {
- headerId: header.to_gid,
- active: false
- }
- end
-
- it 'updates the header active value' do
- expect { subject }
- .to change { header.reload.active }.from(true).to(false)
- .and not_change { header.reload.key }
- .and not_change { header.reload.value }
- end
- end
-
- context 'when the header attributes are invalid' do
- let(:invalid_key_input) do
- {
- headerId: header.to_gid,
- key: '',
- value: 'bar'
- }
- end
-
- let(:mutation) { graphql_mutation(:audit_events_streaming_instance_headers_update, invalid_key_input) }
-
- it 'returns correct errors' do
- subject
-
- expect(mutation_response['errors']).to contain_exactly("Key can't be blank")
- end
-
- it 'returns the unmutated attribute values', :aggregate_failures do
- subject
-
- expect(mutation_response.dig('header', 'key')).to eq('key-1')
- expect(mutation_response.dig('header', 'value')).to eq('bar')
- end
-
- it_behaves_like 'a mutation that does not update a header' do
- let_it_be(:actioner) { admin }
- end
- end
-
- context 'when the header id is wrong' do
- let_it_be(:invalid_header_input) do
- {
- headerId: "gid://gitlab/AuditEvents::Streaming::InstanceHeader/-1",
- key: 'foo',
- value: 'bar'
- }
- end
-
- let(:mutation) { graphql_mutation(:audit_events_streaming_instance_headers_update, invalid_header_input) }
-
- it_behaves_like 'a mutation that returns top-level errors',
- errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
-
- it_behaves_like 'a mutation that does not update a header' do
- let_it_be(:actioner) { admin }
- end
- end
- end
-
- context 'when current user is not instance admin' do
- it_behaves_like 'a mutation that does not update a header' do
- let_it_be(:actioner) { user }
- end
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation that returns top-level errors',
- errors: [Mutations::AuditEvents::Streaming::InstanceHeaders::Base::ERROR_MESSAGE]
-
- it_behaves_like 'a mutation that does not update a header' do
- let_it_be(:actioner) { admin }
- end
- end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/amazon_s3_configurations/create_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/amazon_s3_configurations/create_spec.rb
deleted file mode 100644
index e5ba00234c42a84d9f7ec3d03950e618a358a161..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/mutations/audit_events/amazon_s3_configurations/create_spec.rb
+++ /dev/null
@@ -1,172 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Create Amazon S3 configuration', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:group) { create(:group) }
- let_it_be(:current_user) { create(:user) }
- let_it_be(:destination_name) { 'test_aws_s3_destination' }
- let_it_be(:access_key_id) { 'AKIARANDOMID1234' }
- let_it_be(:secret_access_key) { 'TEST/SECRET/XYZ' }
- let_it_be(:bucket_name) { 'test-bucket' }
- let_it_be(:aws_region) { 'us-east-1' }
-
- let(:mutation) { graphql_mutation(:audit_events_amazon_s3_configuration_create, input) }
- let(:mutation_response) { graphql_mutation_response(:audit_events_amazon_s3_configuration_create) }
-
- let(:input) do
- {
- name: destination_name,
- groupPath: group.full_path,
- accessKeyXid: access_key_id,
- secretAccessKey: secret_access_key,
- bucketName: bucket_name,
- awsRegion: aws_region
- }
- end
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: current_user) }
-
- shared_examples 'creates an audit event' do
- before do
- allow(Gitlab::Audit::Auditor).to receive(:audit)
- end
-
- it 'audits the creation' do
- subject
-
- expect(Gitlab::Audit::Auditor).to have_received(:audit) do |args|
- expect(args[:name]).to eq('amazon_s3_configuration_created')
- expect(args[:author]).to eq(current_user)
- expect(args[:scope]).to eq(group)
- expect(args[:target]).to eq(group)
- expect(args[:message]).to eq("Created Amazon S3 configuration with name: #{destination_name} " \
- "bucket: #{bucket_name} and AWS region: #{aws_region}")
- end
- end
- end
-
- shared_examples 'a mutation that does not create a configuration' do
- it 'does not create the configuration' do
- expect { mutate }
- .not_to change { AuditEvents::AmazonS3Configuration.count }
- end
-
- it 'does not create audit event' do
- expect { mutate }.not_to change { AuditEvent.count }
- end
- end
-
- shared_examples 'an unauthorized mutation that does not create a configuration' do
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not create a configuration'
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is a group owner' do
- before_all do
- group.add_owner(current_user)
- end
-
- it 'resolves group by full path' do
- expect(::Group).to receive(:find_by_full_path).with(group.full_path)
-
- mutate
- end
-
- it 'creates the configuration' do
- expect { mutate }
- .to change { AuditEvents::AmazonS3Configuration.count }.by(1)
-
- config = AuditEvents::AmazonS3Configuration.last
- expect(config.group).to eq(group)
- expect(config.name).to eq(destination_name)
- expect(config.access_key_xid).to eq(access_key_id)
- expect(config.secret_access_key).to eq(secret_access_key)
- expect(config.bucket_name).to eq(bucket_name)
- expect(config.aws_region).to eq(aws_region)
- end
-
- it_behaves_like 'creates an audit event', 'audit_events'
-
- it_behaves_like 'creates a streaming destination',
- AuditEvents::AmazonS3Configuration do
- let(:attributes) do
- {
- legacy: {
- bucket_name: bucket_name,
- aws_region: aws_region,
- access_key_xid: access_key_id,
- secret_access_key: secret_access_key,
- namespace_id: group.id,
- name: destination_name
- },
- streaming: {
- "bucketName" => bucket_name,
- "awsRegion" => aws_region,
- "accessKeyXid" => access_key_id
- }
- }
- end
- end
-
- context 'when there is error while saving' do
- before do
- allow_next_instance_of(AuditEvents::AmazonS3Configuration) do |s3_configuration|
- allow(s3_configuration).to receive(:save).and_return(false)
-
- errors = ActiveModel::Errors.new(s3_configuration).tap { |e| e.add(:aws_region, 'error message') }
- allow(s3_configuration).to receive(:errors).and_return(errors)
- end
- end
-
- it 'does not create the configuration and returns the error' do
- expect { mutate }
- .not_to change { AuditEvents::AmazonS3Configuration.count }
-
- expect(mutation_response).to include(
- 'amazonS3Configuration' => nil,
- 'errors' => ["Aws region error message"])
- end
- end
- end
-
- context 'when current user is a group maintainer' do
- before_all do
- group.add_maintainer(current_user)
- end
-
- it_behaves_like 'an unauthorized mutation that does not create a configuration'
- end
-
- context 'when current user is a group developer' do
- before_all do
- group.add_developer(current_user)
- end
-
- it_behaves_like 'an unauthorized mutation that does not create a configuration'
- end
-
- context 'when current user has guest access' do
- before_all do
- group.add_guest(current_user)
- end
-
- it_behaves_like 'an unauthorized mutation that does not create a configuration'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'an unauthorized mutation that does not create a configuration'
- end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/amazon_s3_configurations/delete_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/amazon_s3_configurations/delete_spec.rb
deleted file mode 100644
index fc9f09992a8b4a91f006f4e60f64f340d2488d24..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/mutations/audit_events/amazon_s3_configurations/delete_spec.rb
+++ /dev/null
@@ -1,87 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Delete Amazon S3 configuration', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:config) { create(:amazon_s3_configuration) }
- let_it_be(:group) { config.group }
- let_it_be(:current_user) { create(:user) }
-
- let(:mutation) { graphql_mutation(:audit_events_amazon_s3_configuration_delete, id: global_id_of(config)) }
- let(:mutation_response) { graphql_mutation_response(:audit_events_amazon_s3_configuration_delete) }
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: current_user) }
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is a group owner' do
- before_all do
- group.add_owner(current_user)
- end
-
- it 'destroys the configuration' do
- expect { mutate }.to change { AuditEvents::AmazonS3Configuration.count }.by(-1)
- end
-
- it 'audits the deletion' do
- expected_hash = {
- name: 'amazon_s3_configuration_deleted',
- author: current_user,
- scope: group,
- target: group,
- message: "Deleted Amazon S3 configuration with name: #{config.name} bucket: " \
- "#{config.bucket_name} and AWS region: #{config.aws_region}"
- }
-
- expect(Gitlab::Audit::Auditor).to receive(:audit).with(hash_including(expected_hash))
-
- mutate
- end
-
- context 'when there is an error during destroy' do
- before do
- expect_next_found_instance_of(AuditEvents::AmazonS3Configuration) do |config|
- allow(config).to receive(:destroy).and_return(false)
- errors = ActiveModel::Errors.new(config).tap { |e| e.add(:base, 'error message') }
- allow(config).to receive(:errors).and_return(errors)
- end
- end
-
- it 'does not destroy the configuration and returns the error' do
- expect { mutate }.not_to change { AuditEvents::AmazonS3Configuration.count }
-
- expect(mutation_response).to include('errors' => ['error message'])
- end
- end
-
- context 'when paired destination exists' do
- let(:paired_model) do
- create(:audit_events_group_external_streaming_destination, :aws, legacy_destination_ref: config.id)
- end
-
- it_behaves_like 'deletes paired destination', :config
- end
- end
-
- context 'when current user is a group maintainer' do
- before_all do
- group.add_maintainer(current_user)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/amazon_s3_configurations/update_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/amazon_s3_configurations/update_spec.rb
deleted file mode 100644
index 6bfa73935bc67d879c8e9c22a00d5d70e3b0cb57..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/mutations/audit_events/amazon_s3_configurations/update_spec.rb
+++ /dev/null
@@ -1,273 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Update Amazon S3 configuration', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be_with_reload(:config) { create(:amazon_s3_configuration) }
- let_it_be_with_reload(:destination) { config }
-
- let_it_be(:group) { config.group }
- let_it_be(:current_user) { create(:user) }
- let_it_be(:updated_access_key_xid) { 'AKIA1234RANDOM5678' }
- let_it_be(:updated_secret_access_key) { 'TEST/SECRET/XYZ/PQR' }
- let_it_be(:updated_bucket_name) { 'test-rspec-bucket' }
- let_it_be(:updated_aws_region) { 'us-east-2' }
- let_it_be(:updated_destination_name) { 'updated_destination_name' }
- let_it_be(:config_gid) { global_id_of(config) }
-
- let(:mutation_name) { :audit_events_amazon_s3_configuration_update }
- let(:mutation_field) { 'amazonS3Configuration' }
- let(:model) { config }
- let(:event_name) { Mutations::AuditEvents::AmazonS3Configurations::Update::UPDATE_EVENT_NAME }
-
- let(:mutation) { graphql_mutation(:audit_events_amazon_s3_configuration_update, input) }
- let(:mutation_response) { graphql_mutation_response(:audit_events_amazon_s3_configuration_update) }
-
- let(:input) do
- {
- id: config_gid,
- accessKeyXid: updated_access_key_xid,
- secretAccessKey: updated_secret_access_key,
- bucketName: updated_bucket_name,
- awsRegion: updated_aws_region,
- name: updated_destination_name,
- active: true
- }
- end
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: current_user) }
-
- shared_examples 'a mutation that does not update the configuration' do
- it 'does not update the configuration' do
- expect { mutate }.not_to change { config.reload.attributes }
- end
-
- it 'does not create audit event' do
- expect { mutate }.not_to change { AuditEvent.count }
- end
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is a group owner' do
- before_all do
- group.add_owner(current_user)
- config.deactivate!
- end
-
- it 'updates the configuration' do
- mutate
-
- config.reload
-
- expect(config.access_key_xid).to eq(updated_access_key_xid)
- expect(config.secret_access_key).to eq(updated_secret_access_key)
- expect(config.bucket_name).to eq(updated_bucket_name)
- expect(config.aws_region).to eq(updated_aws_region)
- expect(config.name).to eq(updated_destination_name)
- expect(config.active).to be(true)
- end
-
- it 'audits the update' do
- Mutations::AuditEvents::AmazonS3Configurations::Update::AUDIT_EVENT_COLUMNS.each do |column|
- message = if column == :secret_access_key
- "Changed #{column}"
- else
- "Changed #{column} from #{config[column]} to #{input[column.to_s.camelize(:lower).to_sym]}"
- end
-
- expected_hash = {
- name: Mutations::AuditEvents::AmazonS3Configurations::Update::UPDATE_EVENT_NAME,
- author: current_user,
- scope: group,
- target: config,
- message: message
- }
-
- expect(Gitlab::Audit::Auditor).to receive(:audit).once.ordered.with(hash_including(expected_hash))
- end
-
- subject
- end
-
- context 'when the fields are updated with existing values' do
- let(:input) do
- {
- id: config_gid,
- accessKeyXid: config.access_key_xid,
- name: config.name
- }
- end
-
- it 'does not audit the event' do
- expect(Gitlab::Audit::Auditor).not_to receive(:audit)
-
- subject
- end
- end
-
- context 'when no fields are provided for update' do
- let(:input) do
- {
- id: config_gid
- }
- end
-
- it_behaves_like 'a mutation that does not update the configuration'
- end
-
- context 'when there is error while updating' do
- before do
- allow_next_instance_of(Mutations::AuditEvents::AmazonS3Configurations::Update) do |mutation|
- allow(mutation).to receive(:authorized_find!).with(id: config_gid).and_return(config)
- end
-
- allow(config).to receive(:update).and_return(false)
-
- errors = ActiveModel::Errors.new(config).tap { |e| e.add(:base, 'error message') }
- allow(config).to receive(:errors).and_return(errors)
- end
-
- it 'does not update the configuration and returns the error' do
- mutate
-
- expect(mutation_response).to include(
- 'amazonS3Configuration' => nil,
- 'errors' => ['error message']
- )
- end
- end
-
- context 'when updating a legacy destination' do
- let(:stream_destination) do
- create(:audit_events_group_external_streaming_destination, :aws, group: group,
- legacy_destination_ref: config.id)
- end
-
- it_behaves_like 'audits legacy active status changes'
-
- it_behaves_like 'updates a streaming destination',
- :config,
- proc {
- {
- legacy: {
- bucket_name: updated_bucket_name,
- aws_region: updated_aws_region,
- access_key_xid: updated_access_key_xid,
- name: updated_destination_name
- },
- streaming: {
- "bucketName" => updated_bucket_name,
- "awsRegion" => updated_aws_region,
- "accessKeyXid" => updated_access_key_xid,
- "name" => updated_destination_name
- }
- }
- }
- end
-
- context 'when only specific fields are updated' do
- before do
- allow(Gitlab::Audit::Auditor).to receive(:audit)
- end
-
- let(:input) do
- {
- id: config_gid,
- bucketName: updated_bucket_name,
- awsRegion: updated_aws_region
- }
- end
-
- it 'only audits the changed attributes' do
- expect(Gitlab::Audit::Auditor).to receive(:audit).with(
- hash_including(
- name: event_name,
- author: current_user,
- scope: group,
- target: config,
- message: "Changed bucket_name from #{config.bucket_name} to #{updated_bucket_name}"
- )
- ).once
-
- expect(Gitlab::Audit::Auditor).to receive(:audit).with(
- hash_including(
- name: event_name,
- author: current_user,
- scope: group,
- target: config,
- message: "Changed aws_region from #{config.aws_region} to #{updated_aws_region}"
- )
- ).once
-
- expect(Gitlab::Audit::Auditor).not_to receive(:audit).with(
- hash_including(message: /Changed access_key_xid/)
- )
-
- expect(Gitlab::Audit::Auditor).not_to receive(:audit).with(
- hash_including(message: /Changed secret_access_key/)
- )
-
- expect(Gitlab::Audit::Auditor).not_to receive(:audit).with(
- hash_including(message: /Changed name/)
- )
-
- expect(Gitlab::Audit::Auditor).not_to receive(:audit).with(
- hash_including(message: /Changed active/)
- )
-
- mutate
-
- config.reload
- expect(config.bucket_name).to eq(updated_bucket_name)
- expect(config.aws_region).to eq(updated_aws_region)
-
- expect(config.access_key_xid).not_to eq(updated_access_key_xid)
- expect(config.secret_access_key).not_to eq(updated_secret_access_key)
- expect(config.name).not_to eq(updated_destination_name)
- end
- end
- end
-
- context 'when current user is a group maintainer' do
- before_all do
- group.add_maintainer(current_user)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not update the configuration'
- end
-
- context 'when current user is a group developer' do
- before_all do
- group.add_developer(current_user)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not update the configuration'
- end
-
- context 'when current user is a group guest' do
- before_all do
- group.add_guest(current_user)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not update the configuration'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not update the configuration'
- end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/external_audit_event_destinations/create_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/external_audit_event_destinations/create_spec.rb
deleted file mode 100644
index 4b6c6ba358977846f242c122eaa1d6be807160f9..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/mutations/audit_events/external_audit_event_destinations/create_spec.rb
+++ /dev/null
@@ -1,303 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Create an external audit event destination', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:group) { create(:group) }
- let_it_be(:owner) { create(:user) }
- let_it_be(:destination_url) { 'https://gitlab.com/example/testendpoint' }
- let_it_be(:destination_name) { 'My Destination' }
- let_it_be(:verification_token) { 'secret-verification-token' }
-
- let(:current_user) { owner }
- let(:mutation) { graphql_mutation(:external_audit_event_destination_create, input) }
- let(:mutation_response) { graphql_mutation_response(:external_audit_event_destination_create) }
-
- let(:input) do
- {
- groupPath: group.full_path,
- destinationUrl: destination_url,
- name: destination_name
- }
- end
-
- let(:invalid_input) do
- {
- groupPath: group.full_path,
- destinationUrl: 'ftp://gitlab.com/example/testendpoint'
- }
- end
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: current_user) }
-
- shared_examples 'creates an audit event' do
- it 'audits the creation' do
- expect { subject }
- .to change { AuditEvent.count }.by(1)
-
- expect(AuditEvent.last.details[:custom_message]).to eq("Create event streaming destination https://gitlab.com/example/testendpoint")
- end
- end
-
- shared_examples 'a mutation that does not create a destination' do
- it 'does not destroy the destination' do
- expect { post_graphql_mutation(mutation, current_user: owner) }
- .not_to change { AuditEvents::ExternalAuditEventDestination.count }
- end
-
- it 'does not audit the creation' do
- expect { post_graphql_mutation(mutation, current_user: owner) }
- .not_to change { AuditEvent.count }
- end
- end
-
- context 'when feature is licensed' do
- subject { post_graphql_mutation(mutation, current_user: owner) }
-
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is a group owner' do
- before do
- group.add_owner(owner)
- end
-
- it 'resolves group by full path' do
- expect(::Group).to receive(:find_by_full_path).with(group.full_path)
-
- subject
- end
-
- it 'creates the destination' do
- expect { subject }
- .to change { AuditEvents::ExternalAuditEventDestination.count }.by(1)
-
- destination = AuditEvents::ExternalAuditEventDestination.last
- expect(destination.group).to eq(group)
- expect(destination.destination_url).to eq(destination_url)
- expect(destination.name).not_to be_empty
- expect(destination.verification_token).to be_present
- end
-
- context 'when overriding default name' do
- let(:name) { 'My Destination' }
-
- let(:input) do
- {
- groupPath: group.full_path,
- destinationUrl: destination_url,
- name: name
- }
- end
-
- it 'creates the destination' do
- expect { subject }
- .to change { AuditEvents::ExternalAuditEventDestination.count }.by(1)
-
- destination = AuditEvents::ExternalAuditEventDestination.last
- expect(destination.group).to eq(group)
- expect(destination.destination_url).to eq(destination_url)
- expect(destination.name).to eq(name)
- expect(destination.verification_token).to be_present
- end
- end
-
- it_behaves_like 'creates an audit event'
-
- it_behaves_like 'creates a streaming destination',
- AuditEvents::ExternalAuditEventDestination do
- let(:attributes) do
- {
- legacy: {
- destination_url: destination_url,
- name: destination_name,
- namespace_id: group.id
- },
- streaming: {
- "url" => destination_url
- }
- }
- end
- end
-
- context 'when overriding verification token' do
- let_it_be(:verification_token) { 'a' * 24 }
-
- let(:input) do
- {
- groupPath: group.full_path,
- destinationUrl: destination_url,
- verificationToken: verification_token
- }
- end
-
- it 'creates the destination' do
- expect { subject }
- .to change { AuditEvents::ExternalAuditEventDestination.count }.by(1)
-
- destination = AuditEvents::ExternalAuditEventDestination.last
- expect(destination.group).to eq(group)
- expect(destination.verification_token).to eq(verification_token)
- expect(destination.destination_url).to eq(destination_url)
- end
-
- it_behaves_like 'creates an audit event'
-
- context 'when verification token is invalid' do
- let(:mutation) { graphql_mutation(:external_audit_event_destination_create, invalid_input) }
-
- context 'when verification token is too short' do
- let(:invalid_input) do
- {
- groupPath: group.full_path,
- destinationUrl: destination_url,
- verificationToken: 'a'
- }
- end
-
- it 'returns correct errors' do
- post_graphql_mutation(mutation, current_user: owner)
-
- expect(graphql_errors).not_to be_empty
- expect(graphql_errors.first['message']).to match(/Verification token is too short/)
- end
-
- it_behaves_like 'a mutation that does not create a destination'
- end
-
- context 'when verification token is too long' do
- let(:invalid_input) do
- {
- groupPath: group.full_path,
- destinationUrl: destination_url,
- verificationToken: 'a' * 25
- }
- end
-
- it 'returns correct errors' do
- post_graphql_mutation(mutation, current_user: owner)
-
- expect(graphql_errors).not_to be_empty
- expect(graphql_errors.first['message']).to match(/Verification token is too long/)
- end
-
- it_behaves_like 'a mutation that does not create a destination'
- end
- end
- end
-
- context 'when destination is invalid' do
- let(:mutation) { graphql_mutation(:external_audit_event_destination_create, invalid_input) }
-
- it 'returns correct errors' do
- post_graphql_mutation(mutation, current_user: owner)
-
- expect(graphql_errors).not_to be_empty
- expect(graphql_errors.first['message']).to match(/Destination url is blocked: Only allowed schemes are http, https/)
- end
-
- it_behaves_like 'a mutation that does not create a destination'
- end
-
- context 'when group is a subgroup' do
- let_it_be(:group) { create(:group, :nested) }
-
- it_behaves_like 'a mutation that does not create a destination'
- end
-
- context 'when ActiveRecord::RecordInvalid exceptions occur' do
- context 'when limit is exceeded' do
- before do
- allow_next_instance_of(AuditEvents::ExternalAuditEventDestination) do |instance|
- allow(instance).to receive(:save!) do
- instance.errors.add(:base, 'Maximum number of external audit event destinations (5) exceeded')
- raise ActiveRecord::RecordInvalid, instance
- end
- end
- end
-
- it 'returns GraphQL error and does not log to Sentry' do
- expect(Gitlab::ErrorTracking).not_to receive(:track_exception)
-
- post_graphql_mutation(mutation, current_user: owner)
-
- expect(graphql_errors).not_to be_empty
- expect(graphql_errors.first['message']).to match(/Maximum number of external audit event destinations/)
- end
-
- it_behaves_like 'a mutation that does not create a destination'
- end
-
- context 'when name is too long' do
- let(:input) do
- {
- groupPath: group.full_path,
- destinationUrl: destination_url,
- name: 'a' * 73 # Exceeds 72 character limit
- }
- end
-
- before do
- allow_next_instance_of(AuditEvents::ExternalAuditEventDestination) do |instance|
- allow(instance).to receive(:save!) do
- instance.errors.add(:name, 'is too long (maximum is 72 characters)')
- raise ActiveRecord::RecordInvalid, instance
- end
- end
- end
-
- it 'returns GraphQL error and does not log to Sentry' do
- expect(Gitlab::ErrorTracking).not_to receive(:track_exception)
-
- post_graphql_mutation(mutation, current_user: owner)
-
- expect(graphql_errors).not_to be_empty
- expect(graphql_errors.first['message']).to match(/is too long/)
- end
-
- it_behaves_like 'a mutation that does not create a destination'
- end
- end
- end
-
- context 'when current user is a group maintainer' do
- before do
- group.add_maintainer(owner)
- end
-
- it_behaves_like 'a mutation that does not create a destination'
- end
-
- context 'when current user is a group developer' do
- before do
- group.add_developer(owner)
- end
-
- it_behaves_like 'a mutation that does not create a destination'
- end
-
- context 'when current user is a group guest' do
- before do
- group.add_guest(owner)
- end
-
- it_behaves_like 'a mutation that does not create a destination'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
-
- it 'does not create the destination' do
- expect { post_graphql_mutation(mutation, current_user: owner) }
- .not_to change { AuditEvents::ExternalAuditEventDestination.count }
- end
- end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/external_audit_event_destinations/destroy_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/external_audit_event_destinations/destroy_spec.rb
deleted file mode 100644
index ab3ff904c7160ba060b52d317dccdfc342c391ac..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/mutations/audit_events/external_audit_event_destinations/destroy_spec.rb
+++ /dev/null
@@ -1,122 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Destroy an external audit event destination', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:group) { create(:group) }
- let_it_be(:owner) { create(:user) }
- let_it_be(:destination) { create(:external_audit_event_destination, group: group) }
-
- let(:current_user) { owner }
-
- let(:input) do
- {
- id: GitlabSchema.id_from_object(destination).to_s
- }
- end
-
- let(:mutation) { graphql_mutation(:external_audit_event_destination_destroy, input) }
-
- let(:mutation_response) { graphql_mutation_response(:external_audit_event_destination_destroy) }
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: current_user) }
-
- shared_examples 'a mutation that does not destroy a destination' do
- it 'does not destroy the destination' do
- expect { post_graphql_mutation(mutation, current_user: owner) }
- .not_to change { AuditEvents::ExternalAuditEventDestination.count }
- end
-
- it 'does not audit the destruction' do
- expect { post_graphql_mutation(mutation, current_user: owner) }
- .not_to change { AuditEvent.count }
- end
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is a group owner but destination belongs to another group' do
- before do
- group.add_owner(owner)
- destination.update!(group: create(:group))
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not destroy a destination'
- end
-
- context 'when current user is a group owner of a different group' do
- before do
- group_2 = create(:group)
- group_2.add_owner(owner)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not destroy a destination'
- end
-
- context 'when current user is a group owner' do
- before do
- group.add_owner(owner)
- end
-
- it 'destroys the destination' do
- expect { post_graphql_mutation(mutation, current_user: owner) }
- .to change { AuditEvents::ExternalAuditEventDestination.count }.by(-1)
- end
-
- it 'audits the destruction' do
- expect { post_graphql_mutation(mutation, current_user: owner) }
- .to change { AuditEvent.count }.by(1)
-
- expect(AuditEvent.last.details[:custom_message]).to match(/Destroy event streaming destination/)
- end
-
- context 'when paired destination exists' do
- let(:paired_model) do
- create(:audit_events_group_external_streaming_destination, :http, legacy_destination_ref: destination.id)
- end
-
- it_behaves_like 'deletes paired destination', :destination
- end
- end
-
- context 'when current user is a group maintainer' do
- before do
- group.add_maintainer(owner)
- end
-
- it_behaves_like 'a mutation that does not destroy a destination'
- end
-
- context 'when current user is a group developer' do
- before do
- group.add_developer(owner)
- end
-
- it_behaves_like 'a mutation that does not destroy a destination'
- end
-
- context 'when current user is a group guest' do
- before do
- group.add_guest(owner)
- end
-
- it_behaves_like 'a mutation that does not destroy a destination'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not destroy a destination'
- end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/external_audit_event_destinations/update_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/external_audit_event_destinations/update_spec.rb
deleted file mode 100644
index f54cc2a8b9e17095c2b77f1bbe4ba4f6a00ea81d..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/mutations/audit_events/external_audit_event_destinations/update_spec.rb
+++ /dev/null
@@ -1,169 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Update an external audit event destination', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:group) { create(:group) }
- let_it_be(:owner) { create(:user) }
- let_it_be(:destination) { create(:external_audit_event_destination, name: "Old Destination", destination_url: "https://example.com/old", group: group) }
- let_it_be(:destination_url) { 'https://example.com/new' }
- let_it_be(:name) { 'New Destination' }
-
- let(:current_user) { owner }
- let(:destination_id) { GitlabSchema.id_from_object(destination) }
-
- let(:input) do
- {
- id: destination_id,
- destinationUrl: destination_url,
- name: name
- }
- end
-
- let(:mutation_name) { :external_audit_event_destination_update }
- let(:mutation_field) { 'externalAuditEventDestination' }
- let(:model) { destination }
- let(:event_name) { 'update_event_streaming_destination' }
- let(:mutation) { graphql_mutation(mutation_name, input) }
- let(:mutation_response) { graphql_mutation_response(mutation_name) }
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: current_user) }
-
- before do
- allow(Gitlab::Audit::Auditor).to receive(:audit).and_call_original
- end
-
- shared_examples 'a mutation that does not update a destination' do
- it 'does not update the destination' do
- expect { post_graphql_mutation(mutation, current_user: owner) }
- .not_to change { destination.reload.destination_url }
- end
-
- it 'does not audit the update' do
- expect { post_graphql_mutation(mutation, current_user: owner) }
- .not_to change { AuditEvent.count }
- end
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is a group owner but destination belongs to another group' do
- before do
- group.add_owner(owner)
- destination.update!(group: create(:group))
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not update a destination'
- end
-
- context 'when current user is a group owner of a different group' do
- before do
- group_2 = create(:group)
- group_2.add_owner(owner)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not update a destination'
- end
-
- context 'when current user is a group owner' do
- before do
- group.add_owner(owner)
- end
-
- it 'updates the destination_url' do
- expect do
- post_graphql_mutation(mutation, current_user: owner)
- end.to change { destination.reload.destination_url }.to(destination_url)
- end
-
- it 'updates the destination name' do
- expect do
- post_graphql_mutation(mutation, current_user: owner)
- end.to change { destination.reload.name }.to(name)
- end
-
- it_behaves_like 'audits update to external streaming destination' do
- let_it_be(:current_user) { owner }
- end
-
- it_behaves_like 'audits legacy active status changes'
-
- context 'when there is no change in values' do
- let(:input) do
- {
- id: destination_id,
- destinationUrl: destination.reload.destination_url
- }
- end
-
- it_behaves_like 'a mutation that does not update a destination'
- end
-
- context 'when updating a legacy destination' do
- let(:stream_destination) do
- create(:audit_events_group_external_streaming_destination, :http, group: group,
- legacy_destination_ref: destination.id)
- end
-
- it_behaves_like 'updates a streaming destination',
- :destination,
- proc {
- {
- legacy: {
- "destination_url" => destination_url,
- "name" => name
- },
- streaming: {
- "url" => destination_url,
- "name" => name
- }
- }
- }
- end
- end
-
- context 'when current user is a group maintainer' do
- before do
- group.add_maintainer(owner)
- end
-
- it_behaves_like 'a mutation that does not update a destination'
- end
-
- context 'when current user is a group developer' do
- before do
- group.add_developer(owner)
- end
-
- it_behaves_like 'a mutation that does not update a destination'
- end
-
- context 'when current user is a group guest' do
- before do
- group.add_guest(owner)
- end
-
- it_behaves_like 'a mutation that does not update a destination'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
-
- it 'does not destroy the destination' do
- expect { post_graphql_mutation(mutation, current_user: owner) }
- .not_to change { destination.reload.destination_url }
- end
- end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/google_cloud_logging_configurations/create_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/google_cloud_logging_configurations/create_spec.rb
deleted file mode 100644
index 6d31029316a4d2619c2bd05e8d906b0fd9d8fe56..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/mutations/audit_events/google_cloud_logging_configurations/create_spec.rb
+++ /dev/null
@@ -1,203 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Create Google Cloud logging configuration', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:group) { create(:group) }
- let_it_be(:owner) { create(:user) }
- let_it_be(:destination_name) { 'my_google_destination' }
- let_it_be(:google_project_id_name) { 'test-project' }
- let_it_be(:client_email) { 'test-email@example.com' }
- let_it_be(:private_key) { OpenSSL::PKey::RSA.new(4096).to_pem }
- let_it_be(:log_id_name) { 'audit_events' }
-
- let(:current_user) { owner }
- let(:mutation) { graphql_mutation(:google_cloud_logging_configuration_create, input) }
- let(:mutation_response) { graphql_mutation_response(:google_cloud_logging_configuration_create) }
-
- let(:input) do
- {
- name: destination_name,
- groupPath: group.full_path,
- googleProjectIdName: google_project_id_name,
- clientEmail: client_email,
- privateKey: private_key
- }
- end
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: owner) }
-
- shared_examples 'creates an audit event' do
- before do
- allow(Gitlab::Audit::Auditor).to receive(:audit)
- end
-
- it 'audits the creation' do
- subject
-
- expect(Gitlab::Audit::Auditor).to have_received(:audit) do |args|
- expect(args[:name]).to eq('google_cloud_logging_configuration_created')
- expect(args[:author]).to eq(current_user)
- expect(args[:scope]).to eq(group)
- expect(args[:target]).to eq(group)
- expect(args[:message]).to eq("Created Google Cloud logging configuration with name: #{destination_name} " \
- "project id: #{google_project_id_name} and log id: #{log_id_name}")
- end
- end
- end
-
- shared_examples 'a mutation that does not create a configuration' do
- it 'does not create the configuration' do
- expect { mutate }
- .not_to change { AuditEvents::GoogleCloudLoggingConfiguration.count }
- end
-
- it 'does not create audit event' do
- expect { mutate }.not_to change { AuditEvent.count }
- end
- end
-
- shared_examples 'an unauthorized mutation that does not create a configuration' do
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not create a configuration'
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is a group owner' do
- before do
- group.add_owner(owner)
- end
-
- it 'resolves group by full path' do
- expect(::Group).to receive(:find_by_full_path).with(group.full_path)
-
- mutate
- end
-
- it 'creates the configuration' do
- expect { mutate }
- .to change { AuditEvents::GoogleCloudLoggingConfiguration.count }.by(1)
-
- config = AuditEvents::GoogleCloudLoggingConfiguration.last
- expect(config.group).to eq(group)
- expect(config.name).to eq(destination_name)
- expect(config.google_project_id_name).to eq(google_project_id_name)
- expect(config.client_email).to eq(client_email)
- expect(config.log_id_name).to eq(log_id_name)
- expect(config.private_key).to eq(private_key)
- end
-
- it_behaves_like 'creates an audit event', 'audit_events'
-
- it_behaves_like 'creates a streaming destination',
- AuditEvents::GoogleCloudLoggingConfiguration do
- let(:attributes) do
- {
- legacy: {
- name: destination_name,
- google_project_id_name: google_project_id_name,
- log_id_name: log_id_name,
- client_email: client_email,
- private_key: private_key,
- namespace_id: group.id
- },
- streaming: {
- "googleProjectIdName" => google_project_id_name,
- "logIdName" => log_id_name,
- "clientEmail" => client_email
- }
- }
- end
- end
-
- context 'when overriding log id name' do
- let_it_be(:log_id_name) { 'test-log-id' }
-
- let(:input) do
- {
- name: destination_name,
- groupPath: group.full_path,
- googleProjectIdName: google_project_id_name,
- clientEmail: client_email,
- privateKey: private_key,
- logIdName: log_id_name
- }
- end
-
- it 'creates the configuration' do
- expect { mutate }
- .to change { AuditEvents::GoogleCloudLoggingConfiguration.count }.by(1)
-
- config = AuditEvents::GoogleCloudLoggingConfiguration.last
- expect(config.group).to eq(group)
- expect(config.name).to eq(destination_name)
- expect(config.google_project_id_name).to eq(google_project_id_name)
- expect(config.client_email).to eq(client_email)
- expect(config.log_id_name).to eq(log_id_name)
- expect(config.private_key).to eq(private_key)
- end
-
- it_behaves_like 'creates an audit event'
- end
-
- context 'when there is error while saving' do
- before do
- allow_next_instance_of(AuditEvents::GoogleCloudLoggingConfiguration) do |instance|
- allow(instance).to receive(:save).and_return(false)
-
- errors = ActiveModel::Errors.new(instance).tap { |e| e.add(:log_id_name, 'error message') }
- allow(instance).to receive(:errors).and_return(errors)
- end
- end
-
- it 'does not create the configuration and returns the error' do
- expect { mutate }
- .not_to change { AuditEvents::GoogleCloudLoggingConfiguration.count }
-
- expect(mutation_response).to include(
- 'googleCloudLoggingConfiguration' => nil,
- 'errors' => ["Log id name error message"]
- )
- end
- end
- end
-
- context 'when current user is a group maintainer' do
- before do
- group.add_maintainer(owner)
- end
-
- it_behaves_like 'an unauthorized mutation that does not create a configuration'
- end
-
- context 'when current user is a group developer' do
- before do
- group.add_developer(owner)
- end
-
- it_behaves_like 'an unauthorized mutation that does not create a configuration'
- end
-
- context 'when current user is a group guest' do
- before do
- group.add_guest(owner)
- end
-
- it_behaves_like 'an unauthorized mutation that does not create a configuration'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'an unauthorized mutation that does not create a configuration'
- end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/google_cloud_logging_configurations/destroy_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/google_cloud_logging_configurations/destroy_spec.rb
deleted file mode 100644
index f41012e905fc95f33e831d1eb0c7fedc81054ec0..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/mutations/audit_events/google_cloud_logging_configurations/destroy_spec.rb
+++ /dev/null
@@ -1,125 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Destroy Google Cloud logging configuration', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:config) { create(:google_cloud_logging_configuration) }
- let_it_be(:group) { config.group }
- let_it_be(:owner) { create(:user) }
-
- let(:current_user) { owner }
- let(:mutation) { graphql_mutation(:google_cloud_logging_configuration_destroy, id: global_id_of(config)) }
- let(:mutation_response) { graphql_mutation_response(:google_cloud_logging_configuration_destroy) }
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: owner) }
-
- shared_examples 'a mutation that does not destroy a configuration' do
- it 'does not destroy the configuration' do
- expect { mutate }
- .not_to change { AuditEvents::GoogleCloudLoggingConfiguration.count }
- end
-
- it 'does not create audit event' do
- expect { mutate }.not_to change { AuditEvent.count }
- end
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is a group owner' do
- before do
- group.add_owner(owner)
- allow(Gitlab::Audit::Auditor).to receive(:audit)
- end
-
- it 'destroys the configuration' do
- expect { mutate }
- .to change { AuditEvents::GoogleCloudLoggingConfiguration.count }.by(-1)
- end
-
- it 'audits the deletion' do
- subject
-
- expect(Gitlab::Audit::Auditor).to have_received(:audit) do |args|
- expect(args[:name]).to eq('google_cloud_logging_configuration_deleted')
- expect(args[:author]).to eq(current_user)
- expect(args[:scope]).to eq(group)
- expect(args[:target]).to eq(group)
- expect(args[:message]).to eq("Deleted Google Cloud logging configuration with name: #{config.name} " \
- "project id: #{config.google_project_id_name} and log id: #{config.log_id_name}")
- end
- end
-
- context 'when there is an error during destroy' do
- before do
- allow_next_instance_of(Mutations::AuditEvents::GoogleCloudLoggingConfigurations::Destroy) do |mutation|
- allow(mutation).to receive(:authorized_find!).and_return(config)
- end
-
- allow(config).to receive(:destroy).and_return(false)
-
- errors = ActiveModel::Errors.new(config).tap { |e| e.add(:base, 'error message') }
- allow(config).to receive(:errors).and_return(errors)
- end
-
- it 'does not destroy the configuration and returns the error' do
- expect { mutate }
- .not_to change { AuditEvents::GoogleCloudLoggingConfiguration.count }
-
- expect(mutation_response).to include(
- 'errors' => ['error message']
- )
- end
- end
-
- context 'when paired destination exists' do
- let(:paired_model) do
- create(:audit_events_group_external_streaming_destination, :gcp, legacy_destination_ref: config.id)
- end
-
- it_behaves_like 'deletes paired destination', :config
- end
- end
-
- context 'when current user is a group maintainer' do
- before do
- group.add_maintainer(owner)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not destroy a configuration'
- end
-
- context 'when current user is a group developer' do
- before do
- group.add_developer(owner)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not destroy a configuration'
- end
-
- context 'when current user is a group guest' do
- before do
- group.add_guest(owner)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not destroy a configuration'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not destroy a configuration'
- end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/google_cloud_logging_configurations/update_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/google_cloud_logging_configurations/update_spec.rb
deleted file mode 100644
index a75531396c3b8cb0da2bdcf5b5fc0dd69cd85e02..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/mutations/audit_events/google_cloud_logging_configurations/update_spec.rb
+++ /dev/null
@@ -1,145 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Update Google Cloud logging configuration', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be_with_reload(:config) { create(:google_cloud_logging_configuration) }
- let_it_be_with_reload(:destination) { config }
-
- let_it_be(:group) { config.group }
- let_it_be(:owner) { create(:user) }
- let_it_be(:updated_google_project_id_name) { 'updated-project' }
- let_it_be(:updated_client_email) { 'updated-email@example.com' }
- let_it_be(:updated_private_key) { OpenSSL::PKey::RSA.new(4096).to_pem }
- let_it_be(:updated_log_id_name) { 'updated_log_id_name' }
- let_it_be(:updated_destination_name) { 'updated_destination_name' }
- let_it_be(:config_gid) { global_id_of(config) }
-
- let(:current_user) { owner }
- let(:mutation) { graphql_mutation(:google_cloud_logging_configuration_update, input) }
- let(:mutation_response) { graphql_mutation_response(:google_cloud_logging_configuration_update) }
- let(:mutation_name) { :google_cloud_logging_configuration_update }
- let(:mutation_field) { 'googleCloudLoggingConfiguration' }
- let(:model) { config }
- let(:event_name) { Mutations::AuditEvents::GoogleCloudLoggingConfigurations::Update::UPDATE_EVENT_NAME }
-
- let(:input) do
- {
- id: config_gid,
- googleProjectIdName: updated_google_project_id_name,
- clientEmail: updated_client_email,
- privateKey: updated_private_key,
- logIdName: updated_log_id_name,
- name: updated_destination_name,
- active: true
- }
- end
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: owner) }
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is a group owner' do
- before do
- group.add_owner(owner)
- allow(Gitlab::Audit::Auditor).to receive(:audit)
- end
-
- it_behaves_like 'entity owner updating google cloud logging configuration' do
- let(:audit_scope) { group }
- let(:audit_event_name) { Mutations::AuditEvents::GoogleCloudLoggingConfigurations::Update::UPDATE_EVENT_NAME }
- end
-
- context 'when there is error while updating' do
- before do
- allow_next_instance_of(Mutations::AuditEvents::GoogleCloudLoggingConfigurations::Update) do |mutation|
- allow(mutation).to receive(:authorized_find!).with(config_gid).and_return(config)
- end
-
- allow(config).to receive(:update).and_return(false)
-
- errors = ActiveModel::Errors.new(config).tap { |e| e.add(:base, 'error message') }
- allow(config).to receive(:errors).and_return(errors)
- end
-
- it 'does not update the configuration and returns the error' do
- mutate
-
- expect(mutation_response).to include(
- 'googleCloudLoggingConfiguration' => nil,
- 'errors' => ['error message']
- )
- end
- end
-
- context 'when updating a legacy destination' do
- let(:stream_destination) do
- create(:audit_events_group_external_streaming_destination, :gcp, group: group,
- legacy_destination_ref: config.id)
- end
-
- it_behaves_like 'audits legacy active status changes'
-
- it_behaves_like 'updates a streaming destination',
- :config,
- proc {
- {
- legacy: {
- "log_id_name" => updated_log_id_name,
- "client_email" => updated_client_email,
- "google_project_id_name" => updated_google_project_id_name,
- "name" => updated_destination_name
- },
- streaming: {
- "logIdName" => updated_log_id_name,
- "clientEmail" => updated_client_email,
- "googleProjectIdName" => updated_google_project_id_name,
- "name" => updated_destination_name
- }
- }
- }
- end
- end
-
- context 'when current user is a group maintainer' do
- before do
- group.add_maintainer(owner)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not update the google cloud logging configuration'
- end
-
- context 'when current user is a group developer' do
- before do
- group.add_developer(owner)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not update the google cloud logging configuration'
- end
-
- context 'when current user is a group guest' do
- before do
- group.add_guest(owner)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not update the google cloud logging configuration'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not update the google cloud logging configuration'
- end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/instance/amazon_s3_configurations/create_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/instance/amazon_s3_configurations/create_spec.rb
deleted file mode 100644
index 03cbdef1b2679a1978880f2eabebe46f80176ba7..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/mutations/audit_events/instance/amazon_s3_configurations/create_spec.rb
+++ /dev/null
@@ -1,150 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Create Instance level Amazon S3 configuration', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:current_user) { create(:admin) }
- let_it_be(:destination_name) { 'test_aws_s3_destination' }
- let_it_be(:access_key_id) { 'AKIARANDOMID1234' }
- let_it_be(:secret_access_key) { 'TEST/SECRET/XYZ' }
- let_it_be(:bucket_name) { 'test-bucket' }
- let_it_be(:aws_region) { 'us-east-1' }
-
- let(:mutation) { graphql_mutation(:audit_events_instance_amazon_s3_configuration_create, input) }
- let(:mutation_response) { graphql_mutation_response(:audit_events_instance_amazon_s3_configuration_create) }
-
- let(:input) do
- {
- name: destination_name,
- accessKeyXid: access_key_id,
- secretAccessKey: secret_access_key,
- bucketName: bucket_name,
- awsRegion: aws_region
- }
- end
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: current_user) }
-
- shared_examples 'creates an audit event' do
- before do
- allow(Gitlab::Audit::Auditor).to receive(:audit)
- end
-
- it 'audits the creation' do
- subject
-
- config = AuditEvents::Instance::AmazonS3Configuration.last
-
- expect(Gitlab::Audit::Auditor).to have_received(:audit) do |args|
- expect(args[:name]).to eq('instance_amazon_s3_configuration_created')
- expect(args[:author]).to eq(current_user)
- expect(args[:scope]).to be_an_instance_of(Gitlab::Audit::InstanceScope)
- expect(args[:target]).to eq(config)
- expect(args[:message])
- .to eq("Created Instance Amazon S3 configuration with name: #{destination_name} " \
- "bucket: #{bucket_name} and AWS region: #{aws_region}")
- end
- end
- end
-
- shared_examples 'a mutation that does not create a configuration' do
- it 'does not create the configuration' do
- expect { mutate }.not_to change { AuditEvents::Instance::AmazonS3Configuration.count }
- end
-
- it 'does not create audit event' do
- expect { mutate }.not_to change { AuditEvent.count }
- end
- end
-
- shared_examples 'an unauthorized mutation that does not create a configuration' do
- it_behaves_like 'a mutation that returns top-level errors',
- errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
-
- it_behaves_like 'a mutation that does not create a configuration'
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is an admin' do
- it 'creates the configuration', :aggregate_failures do
- expect { mutate }.to change { AuditEvents::Instance::AmazonS3Configuration.count }.by(1)
-
- config = AuditEvents::Instance::AmazonS3Configuration.last
-
- expect(config.name).to eq(destination_name)
- expect(config.access_key_xid).to eq(access_key_id)
- expect(config.secret_access_key).to eq(secret_access_key)
- expect(config.bucket_name).to eq(bucket_name)
- expect(config.aws_region).to eq(aws_region)
-
- expect(mutation_response['errors']).to be_empty
- expect(mutation_response['instanceAmazonS3Configuration']['accessKeyXid']).to eq(access_key_id)
- expect(mutation_response['instanceAmazonS3Configuration']['id']).not_to be_empty
- expect(mutation_response['instanceAmazonS3Configuration']['secretAccessKey']).to eq(nil)
- expect(mutation_response['instanceAmazonS3Configuration']['bucketName']).to eq(bucket_name)
- expect(mutation_response['instanceAmazonS3Configuration']['awsRegion']).to eq(aws_region)
- end
-
- it_behaves_like 'creates an audit event', 'audit_events'
-
- it_behaves_like 'creates a streaming destination',
- AuditEvents::Instance::AmazonS3Configuration do
- let(:attributes) do
- {
- legacy: {
- bucket_name: bucket_name,
- aws_region: aws_region,
- access_key_xid: access_key_id,
- secret_access_key: secret_access_key,
- name: destination_name
- },
- streaming: {
- "bucketName" => bucket_name,
- "awsRegion" => aws_region,
- "accessKeyXid" => access_key_id
- }
- }
- end
- end
-
- context 'when there is error while saving' do
- before do
- allow_next_instance_of(AuditEvents::Instance::AmazonS3Configuration) do |s3_configuration|
- allow(s3_configuration).to receive(:save).and_return(false)
- errors = ActiveModel::Errors.new(s3_configuration).tap { |e| e.add(:bucket_name, 'invalid name') }
- allow(s3_configuration).to receive(:errors).and_return(errors)
- end
- end
-
- it 'does not create the configuration and returns the error' do
- expect { mutate }.not_to change { AuditEvents::Instance::AmazonS3Configuration.count }
-
- expect(mutation_response).to include(
- 'instanceAmazonS3Configuration' => nil,
- 'errors' => ["Bucket name invalid name"]
- )
- end
- end
- end
-
- context 'when current user is not an admin' do
- let_it_be(:current_user) { create(:user) }
-
- it_behaves_like 'an unauthorized mutation that does not create a configuration'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'an unauthorized mutation that does not create a configuration'
- end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/instance/amazon_s3_configurations/delete_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/instance/amazon_s3_configurations/delete_spec.rb
deleted file mode 100644
index 1797b2ff2eb18bf33919b064508f59a3287b9fc6..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/mutations/audit_events/instance/amazon_s3_configurations/delete_spec.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Delete instance Amazon S3 configuration', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:config) { create(:instance_amazon_s3_configuration) }
- let_it_be(:current_user) { create(:admin) }
-
- let(:mutation) { graphql_mutation(:audit_events_instance_amazon_s3_configuration_delete, id: global_id_of(config)) }
- let(:mutation_response) { graphql_mutation_response(:audit_events_instance_amazon_s3_configuration_delete) }
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: current_user) }
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is admin' do
- it 'destroys the configuration' do
- expect { mutate }.to change { AuditEvents::Instance::AmazonS3Configuration.count }.by(-1)
- end
-
- it 'audits the deletion' do
- expected_hash = {
- name: 'instance_amazon_s3_configuration_deleted',
- author: current_user,
- scope: an_instance_of(Gitlab::Audit::InstanceScope),
- target: config,
- message: "Deleted Instance Amazon S3 configuration with name: #{config.name} bucket: " \
- "#{config.bucket_name} and AWS region: #{config.aws_region}"
- }
-
- expect(Gitlab::Audit::Auditor).to receive(:audit).with(hash_including(expected_hash))
-
- mutate
- end
-
- context 'when there is an error during destroy' do
- before do
- expect_next_found_instance_of(AuditEvents::Instance::AmazonS3Configuration) do |config|
- allow(config).to receive(:destroy).and_return(false)
- errors = ActiveModel::Errors.new(config).tap { |e| e.add(:base, 'error message') }
- allow(config).to receive(:errors).and_return(errors)
- end
- end
-
- it 'does not destroy the configuration and returns the error' do
- expect { mutate }.not_to change { AuditEvents::Instance::AmazonS3Configuration.count }
-
- expect(mutation_response).to include('errors' => ['error message'])
- end
- end
-
- context 'when paired destination exists' do
- let(:paired_model) do
- create(:audit_events_instance_external_streaming_destination, :aws, legacy_destination_ref: config.id)
- end
-
- it_behaves_like 'deletes paired destination', :config
- end
- end
-
- context 'when current user is not admin' do
- let_it_be(:current_user) { create(:user) }
-
- it_behaves_like 'a mutation on an unauthorized resource'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/instance/amazon_s3_configurations/update_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/instance/amazon_s3_configurations/update_spec.rb
deleted file mode 100644
index e8da826d5d1eb5cc2208a2762e5b60fa9b0bed8e..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/mutations/audit_events/instance/amazon_s3_configurations/update_spec.rb
+++ /dev/null
@@ -1,253 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Update instance Amazon S3 configuration', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be_with_reload(:config) { create(:instance_amazon_s3_configuration) }
- let_it_be_with_reload(:destination) { config }
-
- let_it_be(:current_user) { create(:admin) }
- let_it_be(:updated_access_key_xid) { 'AKIA1234RANDOM5678' }
- let_it_be(:updated_secret_access_key) { 'TEST/SECRET/XYZ/PQR' }
- let_it_be(:updated_bucket_name) { 'test-rspec-bucket' }
- let_it_be(:updated_aws_region) { 'us-east-2' }
- let_it_be(:updated_destination_name) { 'updated_destination_name' }
- let_it_be(:config_gid) { global_id_of(config) }
-
- let(:mutation) { graphql_mutation(:audit_events_instance_amazon_s3_configuration_update, input) }
- let(:mutation_response) { graphql_mutation_response(:audit_events_instance_amazon_s3_configuration_update) }
- let(:mutation_name) { :audit_events_instance_amazon_s3_configuration_update }
- let(:mutation_field) { 'instanceAmazonS3Configuration' }
- let(:model) { config }
- let(:event_name) { Mutations::AuditEvents::Instance::AmazonS3Configurations::Update::UPDATE_EVENT_NAME }
-
- let(:input) do
- {
- id: config_gid,
- accessKeyXid: updated_access_key_xid,
- secretAccessKey: updated_secret_access_key,
- bucketName: updated_bucket_name,
- awsRegion: updated_aws_region,
- name: updated_destination_name,
- active: true
- }
- end
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: current_user) }
-
- shared_examples 'a mutation that does not update the instance Amazon S3 configuration' do
- it 'does not update the configuration' do
- expect { mutate }.not_to change { config.reload.attributes }
- end
-
- it 'does not create audit event' do
- expect { mutate }.not_to change { AuditEvent.count }
- end
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is instance admin' do
- before_all do
- config.deactivate!
- end
-
- before do
- allow(Gitlab::Audit::Auditor).to receive(:audit)
- end
-
- it 'updates the configuration' do
- mutate
-
- config.reload
-
- expect(config.access_key_xid).to eq(updated_access_key_xid)
- expect(config.secret_access_key).to eq(updated_secret_access_key)
- expect(config.bucket_name).to eq(updated_bucket_name)
- expect(config.aws_region).to eq(updated_aws_region)
- expect(config.name).to eq(updated_destination_name)
- expect(config.active).to be(true)
- end
-
- it 'audits the update' do
- Mutations::AuditEvents::Instance::AmazonS3Configurations::Update::AUDIT_EVENT_COLUMNS.each do |column|
- message = if column == :secret_access_key
- "Changed #{column}"
- else
- "Changed #{column} from #{config[column]} to #{input[column.to_s.camelize(:lower).to_sym]}"
- end
-
- expected_hash = {
- name: event_name,
- author: current_user,
- scope: an_instance_of(Gitlab::Audit::InstanceScope),
- target: config,
- message: message
- }
-
- expect(Gitlab::Audit::Auditor).to receive(:audit).once.ordered.with(hash_including(expected_hash))
- end
-
- mutate
- end
-
- context 'when the fields are updated with existing values' do
- let(:input) do
- {
- id: config_gid,
- accessKeyXid: config.access_key_xid,
- name: config.name
- }
- end
-
- it 'does not audit the event' do
- expect(Gitlab::Audit::Auditor).not_to receive(:audit)
-
- mutate
- end
- end
-
- context 'when no fields are provided for update' do
- let(:input) do
- {
- id: config_gid
- }
- end
-
- it_behaves_like 'a mutation that does not update the instance Amazon S3 configuration'
- end
-
- context 'when there is error while updating' do
- before do
- allow_next_instance_of(Mutations::AuditEvents::Instance::AmazonS3Configurations::Update) do |mutation|
- allow(mutation).to receive(:authorized_find!).with(id: config_gid).and_return(config)
- end
-
- allow(config).to receive(:update).and_return(false)
-
- errors = ActiveModel::Errors.new(config).tap { |e| e.add(:base, 'error message') }
- allow(config).to receive(:errors).and_return(errors)
- end
-
- it 'does not update the configuration and returns the error' do
- mutate
-
- expect(mutation_response).to include(
- 'instanceAmazonS3Configuration' => nil,
- 'errors' => ['error message']
- )
- end
- end
-
- context 'when updating a legacy destination' do
- let(:stream_destination) do
- create(:audit_events_instance_external_streaming_destination, :aws,
- legacy_destination_ref: config.id)
- end
-
- it_behaves_like 'audits legacy active status changes'
-
- it_behaves_like 'updates a streaming destination',
- :config,
- proc {
- {
- legacy: {
- bucket_name: updated_bucket_name,
- aws_region: updated_aws_region,
- access_key_xid: updated_access_key_xid,
- name: updated_destination_name
- },
- streaming: {
- "bucketName" => updated_bucket_name,
- "awsRegion" => updated_aws_region,
- "accessKeyXid" => updated_access_key_xid,
- "name" => updated_destination_name
- }
- }
- }
- end
-
- context 'when only specific fields are updated' do
- before do
- allow(Gitlab::Audit::Auditor).to receive(:audit)
- end
-
- let(:input) do
- {
- id: config_gid,
- bucketName: updated_bucket_name,
- awsRegion: updated_aws_region
- }
- end
-
- it 'only audits the changed attributes' do
- expect(Gitlab::Audit::Auditor).to receive(:audit).with(
- hash_including(
- name: event_name,
- author: current_user,
- scope: an_instance_of(Gitlab::Audit::InstanceScope),
- target: config,
- message: "Changed bucket_name from #{config.bucket_name} to #{updated_bucket_name}"
- )
- ).once
-
- expect(Gitlab::Audit::Auditor).to receive(:audit).with(
- hash_including(
- name: event_name,
- author: current_user,
- scope: an_instance_of(Gitlab::Audit::InstanceScope),
- target: config,
- message: "Changed aws_region from #{config.aws_region} to #{updated_aws_region}"
- )
- ).once
-
- expect(Gitlab::Audit::Auditor).not_to receive(:audit).with(
- hash_including(message: /Changed access_key_xid/)
- )
-
- expect(Gitlab::Audit::Auditor).not_to receive(:audit).with(
- hash_including(message: /Changed secret_access_key/)
- )
-
- expect(Gitlab::Audit::Auditor).not_to receive(:audit).with(
- hash_including(message: /Changed name/)
- )
-
- expect(Gitlab::Audit::Auditor).not_to receive(:audit).with(
- hash_including(message: /Changed active/)
- )
-
- mutate
-
- config.reload
- expect(config.bucket_name).to eq(updated_bucket_name)
- expect(config.aws_region).to eq(updated_aws_region)
- expect(config.access_key_xid).not_to eq(updated_access_key_xid)
- expect(config.secret_access_key).not_to eq(updated_secret_access_key)
- expect(config.name).not_to eq(updated_destination_name)
- end
- end
- end
-
- context 'when current user is not instance admin' do
- let_it_be(:current_user) { create(:user) }
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not update the instance Amazon S3 configuration'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation on an unauthorized resource'
- it_behaves_like 'a mutation that does not update the instance Amazon S3 configuration'
- end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/create_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/create_spec.rb
deleted file mode 100644
index 5789b1d35f4619865e61051fe823c8c00eeb4541..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/create_spec.rb
+++ /dev/null
@@ -1,182 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Create Instance level Google Cloud logging configuration', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:admin) { create(:admin) }
- let_it_be(:user) { create(:user) }
- let_it_be(:destination_name) { 'my_google_destination' }
- let_it_be(:google_project_id_name) { 'test-project' }
- let_it_be(:client_email) { 'test-email@example.com' }
- let_it_be(:private_key) { OpenSSL::PKey::RSA.new(4096).to_pem }
- let_it_be(:log_id_name) { 'audit_events' }
-
- let(:current_user) { admin }
- let(:mutation) { graphql_mutation(:instance_google_cloud_logging_configuration_create, input) }
- let(:mutation_response) { graphql_mutation_response(:instance_google_cloud_logging_configuration_create) }
-
- let(:input) do
- {
- name: destination_name,
- googleProjectIdName: google_project_id_name,
- clientEmail: client_email,
- privateKey: private_key
- }
- end
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: current_user) }
-
- shared_examples 'creates an audit event' do
- before do
- allow(Gitlab::Audit::Auditor).to receive(:audit)
- end
-
- it 'audits the creation' do
- subject
-
- config = AuditEvents::Instance::GoogleCloudLoggingConfiguration.last
- expect(Gitlab::Audit::Auditor).to have_received(:audit) do |args|
- expect(args[:name]).to eq('instance_google_cloud_logging_configuration_created')
- expect(args[:author]).to eq(current_user)
- expect(args[:scope]).to be_an_instance_of(Gitlab::Audit::InstanceScope)
- expect(args[:target]).to eq(config)
- expect(args[:message])
- .to eq("Created Instance Google Cloud logging configuration with name: #{destination_name} " \
- "project id: #{google_project_id_name} and log id: #{log_id_name}")
- end
- end
- end
-
- shared_examples 'a mutation that does not create a configuration' do
- it 'does not create the configuration' do
- expect { mutate }
- .not_to change { AuditEvents::Instance::GoogleCloudLoggingConfiguration.count }
- end
-
- it 'does not create audit event' do
- expect { mutate }.not_to change { AuditEvent.count }
- end
- end
-
- shared_examples 'an unauthorized mutation that does not create a configuration' do
- it_behaves_like 'a mutation that returns top-level errors',
- errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
-
- it_behaves_like 'a mutation that does not create a configuration'
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is an admin' do
- it 'creates the configuration' do
- expect { mutate }
- .to change { AuditEvents::Instance::GoogleCloudLoggingConfiguration.count }.by(1)
-
- config = AuditEvents::Instance::GoogleCloudLoggingConfiguration.last
- expect(config.name).to eq(destination_name)
- expect(config.google_project_id_name).to eq(google_project_id_name)
- expect(config.client_email).to eq(client_email)
- expect(config.log_id_name).to eq(log_id_name)
- expect(config.private_key).to eq(private_key)
-
- expect(mutation_response['errors']).to be_empty
- expect(mutation_response['instanceGoogleCloudLoggingConfiguration']['googleProjectIdName'])
- .to eq(google_project_id_name)
- expect(mutation_response['instanceGoogleCloudLoggingConfiguration']['id']).not_to be_empty
- expect(mutation_response['instanceGoogleCloudLoggingConfiguration']['name']).to eq(destination_name)
- expect(mutation_response['instanceGoogleCloudLoggingConfiguration']['clientEmail']).to eq(client_email)
- expect(mutation_response['instanceGoogleCloudLoggingConfiguration']['logIdName']).to eq(log_id_name)
- end
-
- it_behaves_like 'creates an audit event', 'audit_events'
-
- context 'when overriding log id name' do
- let_it_be(:log_id_name) { 'test-log-id' }
-
- let(:input) do
- {
- name: destination_name,
- googleProjectIdName: google_project_id_name,
- clientEmail: client_email,
- privateKey: private_key,
- logIdName: log_id_name
- }
- end
-
- it 'creates the configuration' do
- expect { mutate }
- .to change { AuditEvents::Instance::GoogleCloudLoggingConfiguration.count }.by(1)
-
- config = AuditEvents::Instance::GoogleCloudLoggingConfiguration.last
- expect(config.name).to eq(destination_name)
- expect(config.google_project_id_name).to eq(google_project_id_name)
- expect(config.client_email).to eq(client_email)
- expect(config.log_id_name).to eq(log_id_name)
- expect(config.private_key).to eq(private_key)
- end
-
- it_behaves_like 'creates an audit event'
-
- it_behaves_like 'creates a streaming destination',
- AuditEvents::Instance::GoogleCloudLoggingConfiguration do
- let(:attributes) do
- {
- legacy: {
- name: destination_name,
- google_project_id_name: google_project_id_name,
- log_id_name: log_id_name,
- client_email: client_email,
- private_key: private_key
- },
- streaming: {
- "googleProjectIdName" => google_project_id_name,
- "logIdName" => log_id_name,
- "clientEmail" => client_email
- }
- }
- end
- end
- end
-
- context 'when there is error while saving' do
- before do
- allow_next_instance_of(AuditEvents::Instance::GoogleCloudLoggingConfiguration) do |instance|
- allow(instance).to receive(:save).and_return(false)
-
- errors = ActiveModel::Errors.new(instance).tap { |e| e.add(:log_id_name, 'error message') }
- allow(instance).to receive(:errors).and_return(errors)
- end
- end
-
- it 'does not create the configuration and returns the error' do
- expect { mutate }
- .not_to change { AuditEvents::Instance::GoogleCloudLoggingConfiguration.count }
-
- expect(mutation_response).to include(
- 'instanceGoogleCloudLoggingConfiguration' => nil,
- 'errors' => ["Log id name error message"]
- )
- end
- end
- end
-
- context 'when current user is not an admin' do
- let_it_be(:current_user) { user }
-
- it_behaves_like 'an unauthorized mutation that does not create a configuration'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'an unauthorized mutation that does not create a configuration'
- end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy_spec.rb
deleted file mode 100644
index e9c382386feab31a77fdeb1e90384c927f23bcaa..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy_spec.rb
+++ /dev/null
@@ -1,104 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Destroy Instance Google Cloud logging configuration', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:config) { create(:instance_google_cloud_logging_configuration) }
- let_it_be(:admin) { create(:admin) }
-
- let(:current_user) { admin }
- let(:mutation) { graphql_mutation(:instance_google_cloud_logging_configuration_destroy, id: global_id_of(config)) }
- let(:mutation_response) { graphql_mutation_response(:instance_google_cloud_logging_configuration_destroy) }
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: current_user) }
-
- shared_examples 'a mutation that does not destroy a configuration' do
- it_behaves_like 'a mutation that returns top-level errors',
- errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
-
- it 'does not destroy the configuration' do
- expect { mutate }
- .not_to change { AuditEvents::Instance::GoogleCloudLoggingConfiguration.count }
- end
-
- it 'does not create audit event' do
- expect { mutate }.not_to change { AuditEvent.count }
- end
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is instance admin' do
- before do
- allow(Gitlab::Audit::Auditor).to receive(:audit)
- end
-
- it 'destroys the configuration' do
- expect { mutate }
- .to change { AuditEvents::Instance::GoogleCloudLoggingConfiguration.count }.by(-1)
- end
-
- it 'audits the deletion' do
- subject
-
- expect(Gitlab::Audit::Auditor).to have_received(:audit) do |args|
- expect(args[:name]).to eq('instance_google_cloud_logging_configuration_deleted')
- expect(args[:author]).to eq(current_user)
- expect(args[:scope]).to be_an_instance_of(Gitlab::Audit::InstanceScope)
- expect(args[:target]).to eq(config)
- expect(args[:message])
- .to eq("Deleted Instance Google Cloud logging configuration with name: #{config.name} " \
- "project id: #{config.google_project_id_name} and log id: #{config.log_id_name}")
- end
- end
-
- context 'when there is an error during destroy' do
- before do
- allow_next_instance_of(
- Mutations::AuditEvents::Instance::GoogleCloudLoggingConfigurations::Destroy) do |mutation|
- allow(mutation).to receive(:authorized_find!).and_return(config)
- end
-
- allow(config).to receive(:destroy).and_return(false)
-
- errors = ActiveModel::Errors.new(config).tap { |e| e.add(:base, 'error message') }
- allow(config).to receive(:errors).and_return(errors)
- end
-
- it 'does not destroy the configuration and returns the error' do
- expect { mutate }
- .not_to change { AuditEvents::Instance::GoogleCloudLoggingConfiguration.count }
-
- expect(mutation_response).to include('errors' => ['error message'])
- end
- end
-
- context 'when paired destination exists' do
- let(:paired_model) do
- create(:audit_events_instance_external_streaming_destination, :gcp, legacy_destination_ref: config.id)
- end
-
- it_behaves_like 'deletes paired destination', :config
- end
- end
-
- context 'when current user is not instance admin' do
- let_it_be(:current_user) { create(:user) }
-
- it_behaves_like 'a mutation that does not destroy a configuration'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation that does not destroy a configuration'
- end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/update_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/update_spec.rb
deleted file mode 100644
index 1b080ba3e95a529730376be33913db36df0e4a59..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/update_spec.rb
+++ /dev/null
@@ -1,138 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Update Instance Google Cloud logging configuration', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be_with_reload(:config) { create(:instance_google_cloud_logging_configuration) }
-
- let_it_be(:admin) { create(:admin) }
- let_it_be(:updated_google_project_id_name) { 'updated-project' }
- let_it_be(:updated_client_email) { 'updated-email@example.com' }
- let_it_be(:updated_private_key) { OpenSSL::PKey::RSA.new(4096).to_pem }
- let_it_be(:updated_log_id_name) { 'updated_log_id_name' }
- let_it_be(:updated_destination_name) { 'updated_destination_name' }
- let_it_be(:config_gid) { global_id_of(config) }
-
- let(:current_user) { admin }
- let(:mutation) { graphql_mutation(:instance_google_cloud_logging_configuration_update, input) }
- let(:mutation_response) { graphql_mutation_response(:instance_google_cloud_logging_configuration_update) }
- let(:mutation_name) { :instance_google_cloud_logging_configuration_update }
- let(:mutation_field) { 'instanceGoogleCloudLoggingConfiguration' }
- let(:event_name) { 'instance_google_cloud_logging_configuration_updated' }
-
- let(:model) { config }
-
- let(:input) do
- {
- id: config_gid,
- googleProjectIdName: updated_google_project_id_name,
- clientEmail: updated_client_email,
- privateKey: updated_private_key,
- logIdName: updated_log_id_name,
- name: updated_destination_name,
- active: true
- }
- end
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: current_user) }
-
- shared_examples 'a mutation that does not update the google cloud logging configuration' do
- it 'does not update the configuration' do
- expect { mutate }.not_to change { config.reload.attributes }
- end
-
- it 'does not create audit event' do
- expect { mutate }.not_to change { AuditEvent.count }
- end
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is instance admin' do
- before do
- allow(Gitlab::Audit::Auditor).to receive(:audit)
- end
-
- it_behaves_like 'entity owner updating google cloud logging configuration' do
- let(:audit_scope) { an_instance_of(Gitlab::Audit::InstanceScope) }
- let(:audit_event_name) do
- Mutations::AuditEvents::Instance::GoogleCloudLoggingConfigurations::Update::UPDATE_EVENT_NAME
- end
- end
-
- context 'when there is error while updating' do
- before do
- allow_next_instance_of(
- Mutations::AuditEvents::Instance::GoogleCloudLoggingConfigurations::Update) do |mutation|
- allow(mutation).to receive(:authorized_find!).with(config_gid).and_return(config)
- end
-
- allow(config).to receive(:update).and_return(false)
-
- errors = ActiveModel::Errors.new(config).tap { |e| e.add(:base, 'error message') }
- allow(config).to receive(:errors).and_return(errors)
- end
-
- it 'does not update the configuration and returns the error' do
- mutate
-
- expect(mutation_response).to include(
- 'instanceGoogleCloudLoggingConfiguration' => nil,
- 'errors' => ['error message']
- )
- end
- end
-
- context 'when updating a legacy destination' do
- let(:stream_destination) do
- create(:audit_events_instance_external_streaming_destination, :gcp,
- legacy_destination_ref: config.id)
- end
-
- it_behaves_like 'audits legacy active status changes'
-
- it_behaves_like 'updates a streaming destination',
- :config,
- proc {
- {
- legacy: {
- "log_id_name" => updated_log_id_name,
- "client_email" => updated_client_email,
- "google_project_id_name" => updated_google_project_id_name,
- "name" => updated_destination_name
- },
- streaming: {
- "logIdName" => updated_log_id_name,
- "clientEmail" => updated_client_email,
- "googleProjectIdName" => updated_google_project_id_name,
- "name" => updated_destination_name
- }
- }
- }
- end
- end
-
- context 'when current user is not instance admin' do
- let_it_be(:current_user) { create(:user) }
-
- it_behaves_like 'a mutation that returns top-level errors',
- errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
- it_behaves_like 'a mutation that does not update the google cloud logging configuration'
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation that returns top-level errors',
- errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
- it_behaves_like 'a mutation that does not update the google cloud logging configuration'
- end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/instance_external_audit_event_destinations/create_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/instance_external_audit_event_destinations/create_spec.rb
deleted file mode 100644
index 5e6a5035090920e5a7d89c7f5a5d840f83b980d1..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/mutations/audit_events/instance_external_audit_event_destinations/create_spec.rb
+++ /dev/null
@@ -1,202 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Create an instance external audit event destination', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:admin) { create(:admin) }
- let_it_be(:user) { create(:user) }
- let_it_be(:destination_url) { 'https://gitlab.com/example/testendpoint' }
- let_it_be(:destination_name) { 'My Destination' }
-
- let(:mutation) { graphql_mutation(:instance_external_audit_event_destination_create, input) }
- let(:mutation_response) { graphql_mutation_response(:instance_external_audit_event_destination_create) }
-
- let(:input) do
- {
- destinationUrl: destination_url,
- name: destination_name
- }
- end
-
- let(:invalid_input) do
- {
- destinationUrl: 'ftp://gitlab.com/example/testendpoint'
- }
- end
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: user) }
-
- shared_examples 'creates an audit event' do
- it 'audits the creation' do
- expect(AuditEvents::AuditEventStreamingWorker).to receive(:perform_async).with(
- "create_instance_event_streaming_destination",
- nil,
- anything
- )
-
- expect { subject }
- .to change { AuditEvent.count }.by(1)
-
- expect(AuditEvent.last.details[:custom_message]).to eq("Create instance event streaming destination https://gitlab.com/example/testendpoint")
- end
- end
-
- shared_examples 'a mutation that does not create a destination' do
- subject { post_graphql_mutation(mutation, current_user: current_user) }
-
- it 'does not create the destination' do
- expect { subject }
- .not_to change { AuditEvents::InstanceExternalAuditEventDestination.count }
-
- expect(graphql_data['instanceExternalAuditEventDestination']).to be_nil
- end
-
- it_behaves_like 'a mutation that returns top-level errors',
- errors: ['You do not have access to this mutation.']
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when user is instance admin' do
- subject(:mutate) { post_graphql_mutation(mutation, current_user: admin) }
-
- it 'creates the destination' do
- expect { mutate }
- .to change { AuditEvents::InstanceExternalAuditEventDestination.count }.by(1)
-
- destination = AuditEvents::InstanceExternalAuditEventDestination.last
- expect(destination.destination_url).to eq(destination_url)
- expect(destination.verification_token).to be_present
-
- expect(mutation_response['errors']).to be_empty
- expect(mutation_response['instanceExternalAuditEventDestination']['destinationUrl']).to eq(destination_url)
- expect(mutation_response['instanceExternalAuditEventDestination']['id']).not_to be_empty
- expect(mutation_response['instanceExternalAuditEventDestination']['name']).not_to be_empty
- expect(mutation_response['instanceExternalAuditEventDestination']['verificationToken']).not_to be_empty
- end
-
- context 'when overriding default name' do
- name = "My Destination"
-
- let(:input) do
- {
- destinationUrl: destination_url,
- name: name
- }
- end
-
- it 'creates the destination' do
- expect { mutate }
- .to change { AuditEvents::InstanceExternalAuditEventDestination.count }.by(1)
-
- destination = AuditEvents::InstanceExternalAuditEventDestination.last
- expect(destination.destination_url).to eq(destination_url)
- expect(destination.name).to eq(name)
- end
- end
-
- it_behaves_like 'creates an audit event'
-
- it_behaves_like 'creates a streaming destination',
- AuditEvents::InstanceExternalAuditEventDestination do
- let(:attributes) do
- {
- legacy: {
- destination_url: destination_url,
- name: destination_name
- },
- streaming: {
- "url" => destination_url
- }
- }
- end
- end
-
- context 'when destination is invalid' do
- let(:mutation) { graphql_mutation(:instance_external_audit_event_destination_create, invalid_input) }
-
- it 'returns correct errors' do
- post_graphql_mutation(mutation, current_user: admin)
-
- expect(graphql_errors).not_to be_empty
- expect(graphql_errors.first['message'])
- .to match(/Destination url is blocked: Only allowed schemes are http, https/)
- end
- end
-
- context 'when ActiveRecord::RecordInvalid exceptions occur' do
- context 'when limit is exceeded' do
- before do
- allow_next_instance_of(AuditEvents::InstanceExternalAuditEventDestination) do |instance|
- allow(instance).to receive(:save!) do
- instance.errors.add(:base, 'Maximum number of external audit event destinations (5) exceeded')
- raise ActiveRecord::RecordInvalid, instance
- end
- end
- end
-
- it 'returns GraphQL error and does not log to Sentry' do
- expect(Gitlab::ErrorTracking).not_to receive(:track_exception)
-
- post_graphql_mutation(mutation, current_user: admin)
-
- expect(graphql_errors).not_to be_empty
- expect(graphql_errors.first['message']).to match(/Maximum number of external audit event destinations/)
- end
- end
-
- context 'when name is too long' do
- let(:input) do
- {
- destinationUrl: destination_url,
- name: 'a' * 73 # Exceeds 72 character limit
- }
- end
-
- before do
- allow_next_instance_of(AuditEvents::InstanceExternalAuditEventDestination) do |instance|
- allow(instance).to receive(:save!) do
- instance.errors.add(:name, 'is too long (maximum is 72 characters)')
- raise ActiveRecord::RecordInvalid, instance
- end
- end
- end
-
- it 'returns GraphQL error and does not log to Sentry' do
- expect(Gitlab::ErrorTracking).not_to receive(:track_exception)
-
- post_graphql_mutation(mutation, current_user: admin)
-
- expect(graphql_errors).not_to be_empty
- expect(graphql_errors.first['message']).to match(/is too long/)
- end
- end
- end
- end
-
- context 'when current user is not instance admin' do
- it_behaves_like 'a mutation that does not create a destination' do
- let_it_be(:current_user) { user }
- end
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation that does not create a destination' do
- let_it_be(:current_user) { admin }
- end
-
- it_behaves_like 'a mutation that does not create a destination' do
- let_it_be(:current_user) { user }
- end
- end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/instance_external_audit_event_destinations/destroy_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/instance_external_audit_event_destinations/destroy_spec.rb
deleted file mode 100644
index a7027d1b55c299b3eebfe3ddf5e4efd4a9b47d63..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/mutations/audit_events/instance_external_audit_event_destinations/destroy_spec.rb
+++ /dev/null
@@ -1,109 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Destroy an instance external audit event destination', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:admin) { create(:admin) }
- let_it_be(:user) { create(:user) }
- let_it_be(:destination) { create(:instance_external_audit_event_destination) }
-
- let(:input) do
- {
- id: GitlabSchema.id_from_object(destination).to_s
- }
- end
-
- let(:mutation) { graphql_mutation(:instance_external_audit_event_destination_destroy, input) }
-
- let(:mutation_response) { graphql_mutation_response(:instance_external_audit_event_destination_destroy) }
-
- let_it_be(:current_user) { admin }
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: current_user) }
-
- shared_examples 'a mutation that does not destroy a destination' do
- it 'does not destroy the destination' do
- expect { post_graphql_mutation(mutation, current_user: current_user) }
- .not_to change { AuditEvents::ExternalAuditEventDestination.count }
- end
-
- it_behaves_like 'a mutation that returns top-level errors',
- errors: ['You do not have access to this mutation.']
-
- it 'does not audit the destruction' do
- expect { post_graphql_mutation(mutation, current_user: current_user) }
- .not_to change { AuditEvent.count }
- end
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is an instance admin' do
- it 'destroys the destination' do
- expect { post_graphql_mutation(mutation, current_user: admin) }
- .to change { AuditEvents::InstanceExternalAuditEventDestination.count }.by(-1)
- end
-
- it 'audits the destruction' do
- expect { post_graphql_mutation(mutation, current_user: admin) }
- .to change { AuditEvent.count }.by(1)
-
- expect(AuditEvent.last.details[:custom_message])
- .to eq("Destroy instance event streaming destination #{destination.destination_url}")
- end
-
- context 'when the destination id is invalid' do
- let_it_be(:invalid_destination_input) do
- {
- id: "gid://gitlab/AuditEvents::InstanceExternalAuditEventDestination/-1"
- }
- end
-
- let(:mutation) do
- graphql_mutation(:instance_external_audit_event_destination_destroy, invalid_destination_input)
- end
-
- it 'does not destroy the destination' do
- expect { post_graphql_mutation(mutation, current_user: admin) }
- .to not_change { AuditEvents::InstanceExternalAuditEventDestination.count }
- end
-
- it_behaves_like 'a mutation that returns top-level errors',
- errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
- end
-
- context 'when paired destination exists' do
- let(:paired_model) do
- create(:audit_events_instance_external_streaming_destination, legacy_destination_ref: destination.id)
- end
-
- it_behaves_like 'deletes paired destination', :destination
- end
- end
-
- context 'when current user is not instance admin' do
- before do
- sign_in user
- end
-
- it_behaves_like 'a mutation that does not destroy a destination' do
- let_it_be(:current_user) { user }
- end
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation that does not destroy a destination' do
- let_it_be(:current_user) { admin }
- end
- end
-end
diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/instance_external_audit_event_destinations/update_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/instance_external_audit_event_destinations/update_spec.rb
deleted file mode 100644
index 68259956a101c92efcfb0f6d85778e6c2cd1cd09..0000000000000000000000000000000000000000
--- a/ee/spec/requests/api/graphql/mutations/audit_events/instance_external_audit_event_destinations/update_spec.rb
+++ /dev/null
@@ -1,156 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Update an instance external audit event destination', feature_category: :audit_events do
- include GraphqlHelpers
-
- let_it_be(:old_destination_url) { "https://example.com/old" }
- let_it_be(:admin) { create(:admin) }
- let_it_be(:user) { create(:user) }
- let_it_be_with_reload(:destination) do
- create(:instance_external_audit_event_destination,
- name: "Old Destination",
- destination_url: old_destination_url)
- end
-
- let_it_be(:destination_url) { 'https://example.com/test' }
- let_it_be(:name) { "My Destination" }
-
- let(:input) do
- {
- id: GitlabSchema.id_from_object(destination).to_s,
- destinationUrl: destination_url,
- name: name
- }
- end
-
- let(:mutation_name) { :instance_external_audit_event_destination_update }
- let(:mutation_field) { 'instanceExternalAuditEventDestination' }
- let(:model) { destination }
- let(:mutation) { graphql_mutation(:instance_external_audit_event_destination_update, input) }
- let(:event_name) { 'update_instance_event_streaming_destination' }
-
- let(:mutation_response) { graphql_mutation_response(:instance_external_audit_event_destination_update) }
-
- let_it_be(:current_user) { admin }
-
- subject(:mutate) { post_graphql_mutation(mutation, current_user: current_user) }
-
- shared_examples 'a mutation that does not update destination' do
- it 'does not update the destination' do
- expect { post_graphql_mutation(mutation, current_user: current_user) }
- .not_to change { destination.reload.destination_url }
-
- expect(graphql_data['instanceExternalAuditEventDestination']).to be_nil
- end
-
- it_behaves_like 'a mutation that returns top-level errors',
- errors: ['You do not have access to this mutation.']
-
- it 'does not audit the update' do
- expect { post_graphql_mutation(mutation, current_user: current_user) }
- .not_to change { AuditEvent.count }
- end
- end
-
- context 'when feature is licensed' do
- before do
- stub_licensed_features(external_audit_events: true)
- end
-
- context 'when current user is instance admin' do
- it 'updates the destination with correct response' do
- expect { post_graphql_mutation(mutation, current_user: admin) }
- .to change { destination.reload.destination_url }.to("https://example.com/test")
-
- expect(mutation_response['errors']).to be_empty
- expect(mutation_response['instanceExternalAuditEventDestination']['destinationUrl']).to eq(destination_url)
- expect(mutation_response['instanceExternalAuditEventDestination']['id']).not_to be_empty
- expect(mutation_response['instanceExternalAuditEventDestination']['name']).to eq(name)
- expect(mutation_response['instanceExternalAuditEventDestination']['verificationToken']).not_to be_empty
- end
-
- it_behaves_like 'audits update to external streaming destination'
- it_behaves_like 'audits legacy active status changes'
-
- context 'when destination is same as previous one' do
- let(:input) { super().merge(destinationUrl: old_destination_url) }
-
- it 'updates the destination with correct response' do
- expect { post_graphql_mutation(mutation, current_user: admin) }
- .not_to change { destination.reload.destination_url }
-
- expect(mutation_response['errors']).to be_empty
- expect(mutation_response['instanceExternalAuditEventDestination']['destinationUrl'])
- .to eq(old_destination_url)
- expect(mutation_response['instanceExternalAuditEventDestination']['id']).not_to be_empty
- expect(mutation_response['instanceExternalAuditEventDestination']['verificationToken']).not_to be_empty
- end
- end
-
- context 'when the destination id is invalid' do
- let_it_be(:invalid_destination_input) do
- {
- id: "gid://gitlab/AuditEvents::InstanceExternalAuditEventDestination/-1",
- destinationUrl: destination_url
- }
- end
-
- let(:mutation) do
- graphql_mutation(:instance_external_audit_event_destination_update, invalid_destination_input)
- end
-
- it 'does not update destination' do
- expect { post_graphql_mutation(mutation, current_user: admin) }
- .not_to change { destination.reload.destination_url }
- end
-
- it_behaves_like 'a mutation that returns top-level errors',
- errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
- end
-
- context 'when updating a legacy destination' do
- let(:stream_destination) do
- create(:audit_events_instance_external_streaming_destination, :http,
- legacy_destination_ref: destination.id)
- end
-
- it_behaves_like 'updates a streaming destination',
- :destination,
- proc {
- {
- legacy: {
- "destination_url" => destination_url,
- "name" => name
- },
- streaming: {
- "url" => destination_url,
- "name" => name
- }
- }
- }
- end
- end
-
- context 'when current user is not instance admin' do
- before do
- sign_in user
- end
-
- it_behaves_like 'a mutation that does not update destination' do
- let_it_be(:current_user) { user }
- end
- end
- end
-
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(external_audit_events: false)
- end
-
- it_behaves_like 'a mutation that does not update destination' do
- let_it_be(:current_user) { admin }
- end
- end
-end
diff --git a/ee/spec/support/shared_examples/requests/api/graphql/mutations/audit_events/google_cloud_logging_configurations/update_shared_examples.rb b/ee/spec/support/shared_examples/requests/api/graphql/mutations/audit_events/google_cloud_logging_configurations/update_shared_examples.rb
deleted file mode 100644
index 0c1bc28b85f7138e256f4c5675116b0b2bfaa380..0000000000000000000000000000000000000000
--- a/ee/spec/support/shared_examples/requests/api/graphql/mutations/audit_events/google_cloud_logging_configurations/update_shared_examples.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples 'a mutation that does not update the google cloud logging configuration' do
- it 'does not update the configuration' do
- expect { mutate }.not_to change { config.reload.attributes }
- end
-
- it 'does not create audit event' do
- expect { mutate }.not_to change { AuditEvent.count }
- end
-end
-
-RSpec.shared_examples 'entity owner updating google cloud logging configuration' do
- before do
- config.deactivate!
- end
-
- it 'updates the configuration' do
- mutate
-
- config.reload
-
- expect(config.google_project_id_name).to eq(updated_google_project_id_name)
- expect(config.client_email).to eq(updated_client_email)
- expect(config.private_key).to eq(updated_private_key)
- expect(config.log_id_name).to eq(updated_log_id_name)
- expect(config.name).to eq(updated_destination_name)
- expect(config.active).to be(true)
- end
-
- it 'audits the update' do
- Mutations::AuditEvents::GoogleCloudLoggingConfigurations::CommonUpdate::AUDIT_EVENT_COLUMNS.each do |column|
- message = if column == :private_key
- "Changed #{column}"
- else
- "Changed #{column} from #{config[column]} to #{input[column.to_s.camelize(:lower).to_sym]}"
- end
-
- expected_hash = {
- name: audit_event_name,
- author: current_user,
- scope: audit_scope,
- target: config,
- message: message
- }
-
- expect(Gitlab::Audit::Auditor).to receive(:audit).once.ordered.with(hash_including(expected_hash))
- end
-
- subject
- end
-
- context 'when the fields are updated with existing values' do
- let(:input) do
- {
- id: config_gid,
- googleProjectIdName: config.google_project_id_name,
- name: config.name,
- active: config.active
- }
- end
-
- it 'does not audit the event' do
- expect(Gitlab::Audit::Auditor).not_to receive(:audit)
-
- subject
- end
- end
-
- context 'when no fields are provided for update' do
- let(:input) do
- {
- id: config_gid
- }
- end
-
- it_behaves_like 'a mutation that does not update the google cloud logging configuration'
- end
-end
diff --git a/qa/qa/specs/features/ee/browser_ui/10_software_supply_chain_security/group/group_audit_event_streaming_spec.rb b/qa/qa/specs/features/ee/browser_ui/10_software_supply_chain_security/group/group_audit_event_streaming_spec.rb
index 95f3cb12ff283a711475b14dd39cefaf00601a65..bee18f467543302a07c19f90364c73ffa2243476 100644
--- a/qa/qa/specs/features/ee/browser_ui/10_software_supply_chain_security/group/group_audit_event_streaming_spec.rb
+++ b/qa/qa/specs/features/ee/browser_ui/10_software_supply_chain_security/group/group_audit_event_streaming_spec.rb
@@ -9,10 +9,7 @@ module QA
:skip_live_env, # We need to enable local requests to use a local mock streaming server
# and we can't create top-level groups in the paid tier on production
feature_category: :compliance_management,
- feature_flag: {
- name: 'use_consolidated_audit_event_stream_dest_api',
- scope: :group
- }
+ feature_flag: { name: :disable_audit_event_streaming }
) do
describe 'Group audit event streaming' do
include_context 'with streamed events mock setup'
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index 8b996a49629cc59dac2d8f7162969cfa81c9bbc1..51c984a819501894bd94425f426edd1ab8ff6dd2 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -366,8 +366,6 @@
- './ee/spec/graphql/ee/types/todoable_interface_spec.rb'
- './ee/spec/graphql/ee/types/user_merge_request_interaction_type_spec.rb'
- './ee/spec/graphql/mutations/app_sec/fuzzing/coverage/corpus/create_spec.rb'
-- './ee/spec/graphql/mutations/audit_events/streaming/headers/create_spec.rb'
-- './ee/spec/graphql/mutations/audit_events/streaming/headers/destroy_spec.rb'
- './ee/spec/graphql/mutations/boards/epic_boards/create_spec.rb'
- './ee/spec/graphql/mutations/boards/epic_boards/destroy_spec.rb'
- './ee/spec/graphql/mutations/boards/epic_boards/epic_move_list_spec.rb'
@@ -501,8 +499,6 @@
- './ee/spec/graphql/types/app_sec/fuzzing/api/scan_profile_type_spec.rb'
- './ee/spec/graphql/types/app_sec/fuzzing/coverage/corpus_type_spec.rb'
- './ee/spec/graphql/types/asset_type_spec.rb'
-- './ee/spec/graphql/types/audit_events/exterrnal_audit_event_destination_type_spec.rb'
-- './ee/spec/graphql/types/audit_events/streaming/header_type_spec.rb'
- './ee/spec/graphql/types/boards/board_epic_type_spec.rb'
- './ee/spec/graphql/types/boards/epic_board_type_spec.rb'
- './ee/spec/graphql/types/boards/epic_list_metadata_type_spec.rb'
@@ -1465,9 +1461,6 @@
- './ee/spec/requests/api/graphql/analytics/devops_adoption/enabled_namespaces_spec.rb'
- './ee/spec/requests/api/graphql/app_sec/fuzzing/api/ci_configuration_type_spec.rb'
- './ee/spec/requests/api/graphql/app_sec/fuzzing/coverage/corpus_type_spec.rb'
-- './ee/spec/requests/api/graphql/audit_events/streaming/headers/create_spec.rb'
-- './ee/spec/requests/api/graphql/audit_events/streaming/headers/destroy_spec.rb'
-- './ee/spec/requests/api/graphql/audit_events/streaming/headers/update_spec.rb'
- './ee/spec/requests/api/graphql/boards/board_list_query_spec.rb'
- './ee/spec/requests/api/graphql/boards/board_lists_query_spec.rb'
- './ee/spec/requests/api/graphql/boards/boards_query_spec.rb'
@@ -1506,9 +1499,6 @@
- './ee/spec/requests/api/graphql/mutations/analytics/devops_adoption/enabled_namespaces/bulk_enable_spec.rb'
- './ee/spec/requests/api/graphql/mutations/analytics/devops_adoption/enabled_namespaces/disable_spec.rb'
- './ee/spec/requests/api/graphql/mutations/analytics/devops_adoption/enabled_namespaces/enable_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/audit_events/external_audit_event_destinations/create_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/audit_events/external_audit_event_destinations/destroy_spec.rb'
-- './ee/spec/requests/api/graphql/mutations/audit_events/external_audit_event_destinations/update_spec.rb'
- './ee/spec/requests/api/graphql/mutations/boards/create_spec.rb'
- './ee/spec/requests/api/graphql/mutations/boards/epic_boards/create_spec.rb'
- './ee/spec/requests/api/graphql/mutations/boards/epic_boards/destroy_spec.rb'