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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-09-26 00:11:07 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-09-26 00:11:07 +0300
commit1c61faf876f0da721dde9dc52fb28ab0e7330c6d (patch)
treeb2c5330325d7cc53098dbcf53de1186f5b54bfba
parenteccc2ec564f427460be5ffa6f9a6fb25f3f7fe6d (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.checksum3
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/ci/job_details/graphql/mutations/job_retry_with_variables.mutation.graphql2
-rw-r--r--app/assets/javascripts/ci/jobs_page/graphql/mutations/job_retry.mutation.graphql2
-rw-r--r--app/assets/javascripts/ci/merge_requests/graphql/mutations/retry_mr_failed_job.mutation.graphql2
-rw-r--r--app/assets/javascripts/ci/pipeline_details/jobs/graphql/mutations/retry_failed_job.mutation.graphql2
-rw-r--r--app/controllers/oauth/authorizations_controller.rb4
-rw-r--r--app/graphql/mutations/ci/job/retry.rb6
-rw-r--r--app/mailers/emails/profile.rb16
-rw-r--r--app/mailers/previews/notify_preview.rb8
-rw-r--r--app/services/applications/create_service.rb3
-rw-r--r--app/services/authorizations/notification_service.rb20
-rw-r--r--app/services/notification_service.rb14
-rw-r--r--app/views/notify/application_authorized_email.html.haml5
-rw-r--r--app/views/notify/application_authorized_email.text.erb7
-rw-r--r--app/views/notify/application_created_email.html.haml5
-rw-r--r--app/views/notify/application_created_email.text.erb7
-rw-r--r--config/events/g_project_management_issue_assignee_changed.yml25
-rw-r--r--config/events/g_project_management_issue_cloned.yml25
-rw-r--r--config/events/g_project_management_issue_closed.yml25
-rw-r--r--config/events/g_project_management_issue_comment_added.yml25
-rw-r--r--config/events/g_project_management_issue_comment_edited.yml25
-rw-r--r--config/events/g_project_management_issue_comment_removed.yml25
-rw-r--r--config/events/g_project_management_issue_cross_referenced.yml25
-rw-r--r--config/events/g_project_management_issue_description_changed.yml25
-rw-r--r--config/events/g_project_management_issue_design_comments_removed.yml25
-rw-r--r--config/events/g_project_management_issue_designs_added.yml25
-rw-r--r--config/events/g_project_management_issue_designs_modified.yml25
-rw-r--r--config/events/g_project_management_issue_designs_removed.yml25
-rw-r--r--config/events/g_project_management_issue_due_date_changed.yml25
-rw-r--r--config/events/g_project_management_issue_label_changed.yml25
-rw-r--r--config/events/g_project_management_issue_locked.yml25
-rw-r--r--config/events/g_project_management_issue_made_confidential.yml25
-rw-r--r--config/events/g_project_management_issue_made_visible.yml25
-rw-r--r--config/events/g_project_management_issue_marked_as_duplicate.yml25
-rw-r--r--config/events/g_project_management_issue_milestone_changed.yml25
-rw-r--r--config/events/g_project_management_issue_moved.yml25
-rw-r--r--config/events/g_project_management_issue_related.yml25
-rw-r--r--config/events/g_project_management_issue_reopened.yml25
-rw-r--r--config/events/g_project_management_issue_time_estimate_changed.yml25
-rw-r--r--config/events/g_project_management_issue_time_spent_changed.yml25
-rw-r--r--config/events/g_project_management_issue_title_changed.yml25
-rw-r--r--config/events/g_project_management_issue_unlocked.yml25
-rw-r--r--config/events/g_project_management_issue_unrelated.yml25
-rw-r--r--doc/api/graphql/reference/index.md12
-rw-r--r--doc/architecture/blueprints/email_ingestion/index.md169
-rw-r--r--doc/user/ai_features.md2
-rw-r--r--doc/user/discussions/index.md2
-rw-r--r--doc/user/project/system_notes.md9
-rw-r--r--locale/gitlab.pot15
-rw-r--r--spec/frontend/boards/board_list_helper.js2
-rw-r--r--spec/frontend/boards/board_list_spec.js2
-rw-r--r--spec/mailers/emails/profile_spec.rb40
-rw-r--r--spec/requests/api/graphql/mutations/ci/job/retry_spec.rb28
-rw-r--r--spec/services/applications/create_service_spec.rb8
-rw-r--r--spec/services/authorizations/notification_service_spec.rb19
57 files changed, 1072 insertions, 23 deletions
diff --git a/Gemfile b/Gemfile
index 9573ba39b06..ef7c3372eaa 100644
--- a/Gemfile
+++ b/Gemfile
@@ -224,7 +224,7 @@ gem 'rack', '~> 2.2.8'
gem 'rack-timeout', '~> 0.6.3', require: 'rack/timeout/base'
group :puma do
- gem 'puma', '~> 6.4', require: false
+ gem 'puma', '~> 6.3', '>= 6.3.1', require: false
gem 'sd_notify', '~> 0.1.0', require: false
end
diff --git a/Gemfile.checksum b/Gemfile.checksum
index 82d8344329c..71499cec4cb 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -460,8 +460,7 @@
{"name":"pry-rails","version":"0.3.9","platform":"ruby","checksum":"468662575abb6b67f4a9831219f99290d5eae7bf186e64dd810d0a3e4a8cc4b1"},
{"name":"pry-shell","version":"0.6.4","platform":"ruby","checksum":"ad024882d29912b071a7de65ebea538b242d2dc1498c60c7c2352ef94769f208"},
{"name":"public_suffix","version":"5.0.0","platform":"ruby","checksum":"26ee4fbce33ada25eb117ac71f2c24bf4d8b3414ab6b34f05b4708a3e90f1c6b"},
-{"name":"puma","version":"6.4.0","platform":"java","checksum":"eb27679e9e665882bab85dfa84704b0615b4f77cec46de014f05b90a5ab36cfe"},
-{"name":"puma","version":"6.4.0","platform":"ruby","checksum":"d5dda11362744df9f4694708a62e3cfddf72eba7498c16016ebbb30f106712f9"},
+{"name":"puma","version":"6.3.1","platform":"ruby","checksum":"ec989c775f88f5cbea0c7178b94ed9ff44797241f9245d353d1774a845e78df4"},
{"name":"pyu-ruby-sasl","version":"0.0.3.3","platform":"ruby","checksum":"5683a6bc5738db5a1bf5ceddeaf545405fb241b4184dd4f2587e679a7e9497e5"},
{"name":"raabro","version":"1.4.0","platform":"ruby","checksum":"d4fa9ff5172391edb92b242eed8be802d1934b1464061ae5e70d80962c5da882"},
{"name":"racc","version":"1.6.2","platform":"java","checksum":"0880781e7dfde09e665d0b6160b583e01ed52fcc2955d7891447d33c2d1d2cf1"},
diff --git a/Gemfile.lock b/Gemfile.lock
index c5883ced594..daf8baa6421 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1227,7 +1227,7 @@ GEM
tty-markdown
tty-prompt
public_suffix (5.0.0)
- puma (6.4.0)
+ puma (6.3.1)
nio4r (~> 2.0)
pyu-ruby-sasl (0.0.3.3)
raabro (1.4.0)
@@ -1952,7 +1952,7 @@ DEPENDENCIES
pry-byebug
pry-rails (~> 0.3.9)
pry-shell (~> 0.6.4)
- puma (~> 6.4)
+ puma (~> 6.3, >= 6.3.1)
rack (~> 2.2.8)
rack-attack (~> 6.7.0)
rack-cors (~> 2.0.1)
diff --git a/app/assets/javascripts/ci/job_details/graphql/mutations/job_retry_with_variables.mutation.graphql b/app/assets/javascripts/ci/job_details/graphql/mutations/job_retry_with_variables.mutation.graphql
index cd66a30ce63..b7c93c2830a 100644
--- a/app/assets/javascripts/ci/job_details/graphql/mutations/job_retry_with_variables.mutation.graphql
+++ b/app/assets/javascripts/ci/job_details/graphql/mutations/job_retry_with_variables.mutation.graphql
@@ -1,6 +1,6 @@
#import "~/ci/job_details/graphql/fragments/ci_job.fragment.graphql"
-mutation retryJobWithVariables($id: CiBuildID!, $variables: [CiVariableInput!]) {
+mutation retryJobWithVariables($id: CiProcessableID!, $variables: [CiVariableInput!]) {
jobRetry(input: { id: $id, variables: $variables }) {
job {
...BaseCiJob
diff --git a/app/assets/javascripts/ci/jobs_page/graphql/mutations/job_retry.mutation.graphql b/app/assets/javascripts/ci/jobs_page/graphql/mutations/job_retry.mutation.graphql
index 6e51f9a20fa..077c8e31749 100644
--- a/app/assets/javascripts/ci/jobs_page/graphql/mutations/job_retry.mutation.graphql
+++ b/app/assets/javascripts/ci/jobs_page/graphql/mutations/job_retry.mutation.graphql
@@ -1,6 +1,6 @@
#import "../fragments/job.fragment.graphql"
-mutation retryJob($id: CiBuildID!) {
+mutation retryJob($id: CiProcessableID!) {
jobRetry(input: { id: $id }) {
job {
...Job
diff --git a/app/assets/javascripts/ci/merge_requests/graphql/mutations/retry_mr_failed_job.mutation.graphql b/app/assets/javascripts/ci/merge_requests/graphql/mutations/retry_mr_failed_job.mutation.graphql
index 022d461dbec..f6de6cde9d0 100644
--- a/app/assets/javascripts/ci/merge_requests/graphql/mutations/retry_mr_failed_job.mutation.graphql
+++ b/app/assets/javascripts/ci/merge_requests/graphql/mutations/retry_mr_failed_job.mutation.graphql
@@ -1,4 +1,4 @@
-mutation retryMrFailedJob($id: CiBuildID!) {
+mutation retryMrFailedJob($id: CiProcessableID!) {
jobRetry(input: { id: $id }) {
errors
}
diff --git a/app/assets/javascripts/ci/pipeline_details/jobs/graphql/mutations/retry_failed_job.mutation.graphql b/app/assets/javascripts/ci/pipeline_details/jobs/graphql/mutations/retry_failed_job.mutation.graphql
index 1955cc9b0ac..b60afe51dd2 100644
--- a/app/assets/javascripts/ci/pipeline_details/jobs/graphql/mutations/retry_failed_job.mutation.graphql
+++ b/app/assets/javascripts/ci/pipeline_details/jobs/graphql/mutations/retry_failed_job.mutation.graphql
@@ -1,4 +1,4 @@
-mutation retryFailedJob($id: CiBuildID!) {
+mutation retryFailedJob($id: CiProcessableID!) {
jobRetry(input: { id: $id }) {
job {
id
diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb
index a541e7e703f..9d85ec081d7 100644
--- a/app/controllers/oauth/authorizations_controller.rb
+++ b/app/controllers/oauth/authorizations_controller.rb
@@ -14,14 +14,18 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
# include the call to session.delete
def new
if pre_auth.authorizable?
+
if skip_authorization? || (matching_token? && pre_auth.client.application.confidential?)
auth = authorization.authorize
parsed_redirect_uri = URI.parse(auth.redirect_uri)
session.delete(:user_return_to)
+ Authorizations::NotificationService.new(current_user).execute
+
render "doorkeeper/authorizations/redirect", locals: { redirect_uri: parsed_redirect_uri }, layout: false
else
redirect_uri = URI(authorization.authorize.redirect_uri)
allow_redirect_uri_form_action(redirect_uri.scheme)
+ Authorizations::NotificationService.new(current_user).execute
render "doorkeeper/authorizations/new"
end
diff --git a/app/graphql/mutations/ci/job/retry.rb b/app/graphql/mutations/ci/job/retry.rb
index bfb9b902cc5..5ccc33de33e 100644
--- a/app/graphql/mutations/ci/job/retry.rb
+++ b/app/graphql/mutations/ci/job/retry.rb
@@ -6,6 +6,12 @@ module Mutations
class Retry < Base
graphql_name 'JobRetry'
+ JobID = ::Types::GlobalIDType[::Ci::Processable]
+
+ argument :id, JobID,
+ required: true,
+ description: 'ID of the job to mutate.'
+
field :job,
Types::Ci::JobType,
null: true,
diff --git a/app/mailers/emails/profile.rb b/app/mailers/emails/profile.rb
index a9e1efbdd5d..e65e432125e 100644
--- a/app/mailers/emails/profile.rb
+++ b/app/mailers/emails/profile.rb
@@ -82,6 +82,22 @@ module Emails
)
end
+ def application_created_email(user)
+ return unless user
+
+ @user = user
+
+ email_with_layout(to: @user.notification_email_or_default, subject: subject(_("A new application has been created")))
+ end
+
+ def application_authorized_email(user)
+ return unless user
+
+ @user = user
+
+ email_with_layout(to: @user.notification_email_or_default, subject: subject(_("An application has been authorized")))
+ end
+
def access_token_created_email(user, token_name)
return unless user&.active?
diff --git a/app/mailers/previews/notify_preview.rb b/app/mailers/previews/notify_preview.rb
index 638df56b770..cc69339f6ae 100644
--- a/app/mailers/previews/notify_preview.rb
+++ b/app/mailers/previews/notify_preview.rb
@@ -319,6 +319,14 @@ class NotifyPreview < ActionMailer::Preview
Notify.github_gists_import_errors_email(user.id, { '12345' => 'Snippet maximum file count exceeded', '67890' => 'error message 2' }).message
end
+ def application_authorized_email
+ Notify.application_authorized_email(user).message
+ end
+
+ def application_created_email
+ Notify.application_created_email(user).message
+ end
+
private
def project
diff --git a/app/services/applications/create_service.rb b/app/services/applications/create_service.rb
index 96cde9057c7..3737960c941 100644
--- a/app/services/applications/create_service.rb
+++ b/app/services/applications/create_service.rb
@@ -2,6 +2,8 @@
module Applications
class CreateService
+ include BaseServiceUtility
+
attr_reader :current_user, :params
def initialize(current_user, params)
@@ -20,6 +22,7 @@ module Applications
end
@application.save
+ notification_service.application_created(current_user)
@application
end
end
diff --git a/app/services/authorizations/notification_service.rb b/app/services/authorizations/notification_service.rb
new file mode 100644
index 00000000000..0a21b699c5a
--- /dev/null
+++ b/app/services/authorizations/notification_service.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Authorizations
+ class NotificationService
+ include BaseServiceUtility
+
+ attr_reader :current_user
+
+ def initialize(current_user)
+ @current_user = current_user
+ end
+
+ # EE would override and use `request` arg
+ def execute
+ notification_service.application_authorized(current_user)
+ end
+ end
+end
+
+Authorizations::NotificationService.prepend_mod_with('Authorizaions::NotificationService')
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index f1781b3d3c5..b0de62c299a 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -88,6 +88,20 @@ class NotificationService
end
end
+ # Notify the owner of the account when a new application is created
+ def application_created(user)
+ return unless user.can?(:receive_notifications)
+
+ mailer.application_created_email(user).deliver_now
+ end
+
+ # Notify the owner of the account when an application is authorized
+ def application_authorized(user)
+ return unless user.can?(:receive_notifications)
+
+ mailer.application_authorized_email(user).deliver_now
+ end
+
# Notify the owner of the account when a new personal access token is created
def access_token_created(user, token_name)
return unless user.can?(:receive_notifications)
diff --git a/app/views/notify/application_authorized_email.html.haml b/app/views/notify/application_authorized_email.html.haml
new file mode 100644
index 00000000000..0e4635422cc
--- /dev/null
+++ b/app/views/notify/application_authorized_email.html.haml
@@ -0,0 +1,5 @@
+%p
+ = _('Hi %{username}!') % { username: sanitize_name(@user.name) }
+%p
+ - docs_link = link_to _("documentation"), help_page_url('integration/oauth_provider', anchor: 'view-all-authorized-applications'), target: '_blank', rel: 'noopener noreferrer'
+ = s_('Notify|A new OAuth 2 application has been authorized. If you did not expect this, check the authorized OAuth 2 applications %{docs_link}.').html_safe % { docs_link: docs_link }
diff --git a/app/views/notify/application_authorized_email.text.erb b/app/views/notify/application_authorized_email.text.erb
new file mode 100644
index 00000000000..7bc550e0854
--- /dev/null
+++ b/app/views/notify/application_authorized_email.text.erb
@@ -0,0 +1,7 @@
+<p>
+ <%= _('Hi %{username}!') % { username: sanitize_name(@user.name) } %>
+</p>
+<p>
+ <% docs_link = link_to _("documentation"), help_page_url('integration/oauth_provider', anchor: 'view-all-authorized-applications'), target: '_blank', rel: 'noopener noreferrer' %>
+ <%= s_('Notify|A new OAuth 2 application has been authorized. If you did not expect this, check the authorized OAuth 2 applications %{docs_link}.').html_safe % { docs_link: docs_link } %>
+</p> \ No newline at end of file
diff --git a/app/views/notify/application_created_email.html.haml b/app/views/notify/application_created_email.html.haml
new file mode 100644
index 00000000000..1113cc13d0b
--- /dev/null
+++ b/app/views/notify/application_created_email.html.haml
@@ -0,0 +1,5 @@
+%p
+ = _('Hi %{username}!') % { username: sanitize_name(@user.name) }
+%p
+ - docs_link = link_to _("documentation"), help_page_url('integration/oauth_provider'), target: '_blank', rel: 'noopener noreferrer'
+ = s_('Notify|A new OAuth 2 application has been created. If you did not create this application, check the OAuth 2 applications %{docs_link}.').html_safe % { docs_link: docs_link }
diff --git a/app/views/notify/application_created_email.text.erb b/app/views/notify/application_created_email.text.erb
new file mode 100644
index 00000000000..8164f342f9a
--- /dev/null
+++ b/app/views/notify/application_created_email.text.erb
@@ -0,0 +1,7 @@
+<p>
+ <%= _('Hi %{username}!') % { username: sanitize_name(@user.name) } %>
+</p>
+<p>
+ <% docs_link = link_to _("documentation"), help_page_url('integration/oauth_provider'), target: '_blank', rel: 'noopener noreferrer' %>
+ <%= s_('Notify|A new OAuth 2 application has been created. If you did not create this application, check the OAuth 2 applications %{docs_link}.').html_safe % { docs_link: docs_link } %>
+</p> \ No newline at end of file
diff --git a/config/events/g_project_management_issue_assignee_changed.yml b/config/events/g_project_management_issue_assignee_changed.yml
new file mode 100644
index 00000000000..37b198b29f9
--- /dev/null
+++ b/config/events/g_project_management_issue_assignee_changed.yml
@@ -0,0 +1,25 @@
+---
+description: An issue assignee was changed
+category: InternalEventTracking
+action: g_project_management_issue_assignee_changed
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_cloned.yml b/config/events/g_project_management_issue_cloned.yml
new file mode 100644
index 00000000000..b4caea8fd9b
--- /dev/null
+++ b/config/events/g_project_management_issue_cloned.yml
@@ -0,0 +1,25 @@
+---
+description: An issue was cloned
+category: InternalEventTracking
+action: g_project_management_issue_cloned
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_closed.yml b/config/events/g_project_management_issue_closed.yml
new file mode 100644
index 00000000000..d893bf61880
--- /dev/null
+++ b/config/events/g_project_management_issue_closed.yml
@@ -0,0 +1,25 @@
+---
+description: An issue was closed
+category: InternalEventTracking
+action: g_project_management_issue_closed
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_comment_added.yml b/config/events/g_project_management_issue_comment_added.yml
new file mode 100644
index 00000000000..9059f258717
--- /dev/null
+++ b/config/events/g_project_management_issue_comment_added.yml
@@ -0,0 +1,25 @@
+---
+description: A comment on an issue was added
+category: InternalEventTracking
+action: g_project_management_issue_comment_added
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_comment_edited.yml b/config/events/g_project_management_issue_comment_edited.yml
new file mode 100644
index 00000000000..b4e80af2465
--- /dev/null
+++ b/config/events/g_project_management_issue_comment_edited.yml
@@ -0,0 +1,25 @@
+---
+description: A comment on an issue was edited
+category: InternalEventTracking
+action: g_project_management_issue_comment_edited
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_comment_removed.yml b/config/events/g_project_management_issue_comment_removed.yml
new file mode 100644
index 00000000000..bf905fa95fa
--- /dev/null
+++ b/config/events/g_project_management_issue_comment_removed.yml
@@ -0,0 +1,25 @@
+---
+description: A comment on an issue was removed
+category: InternalEventTracking
+action: g_project_management_issue_comment_removed
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_cross_referenced.yml b/config/events/g_project_management_issue_cross_referenced.yml
new file mode 100644
index 00000000000..696e25b5c96
--- /dev/null
+++ b/config/events/g_project_management_issue_cross_referenced.yml
@@ -0,0 +1,25 @@
+---
+description: An issue was referenced from somewhere else
+category: InternalEventTracking
+action: g_project_management_issue_cross_referenced
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_description_changed.yml b/config/events/g_project_management_issue_description_changed.yml
new file mode 100644
index 00000000000..dc0a4d5f48d
--- /dev/null
+++ b/config/events/g_project_management_issue_description_changed.yml
@@ -0,0 +1,25 @@
+---
+description: Description of an issue changed
+category: InternalEventTracking
+action: g_project_management_issue_description_changed
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_design_comments_removed.yml b/config/events/g_project_management_issue_design_comments_removed.yml
new file mode 100644
index 00000000000..22cfbc22f0b
--- /dev/null
+++ b/config/events/g_project_management_issue_design_comments_removed.yml
@@ -0,0 +1,25 @@
+---
+description: A comment on a design was removed
+category: InternalEventTracking
+action: g_project_management_issue_design_comments_removed
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: product_planning
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_designs_added.yml b/config/events/g_project_management_issue_designs_added.yml
new file mode 100644
index 00000000000..8d8e26d2796
--- /dev/null
+++ b/config/events/g_project_management_issue_designs_added.yml
@@ -0,0 +1,25 @@
+---
+description: A design was added to an issue
+category: InternalEventTracking
+action: g_project_management_issue_designs_added
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_designs_modified.yml b/config/events/g_project_management_issue_designs_modified.yml
new file mode 100644
index 00000000000..bb3423d21ac
--- /dev/null
+++ b/config/events/g_project_management_issue_designs_modified.yml
@@ -0,0 +1,25 @@
+---
+description: A design was modified on an issue
+category: InternalEventTracking
+action: g_project_management_issue_designs_modified
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_designs_removed.yml b/config/events/g_project_management_issue_designs_removed.yml
new file mode 100644
index 00000000000..b6416637132
--- /dev/null
+++ b/config/events/g_project_management_issue_designs_removed.yml
@@ -0,0 +1,25 @@
+---
+description: A design wes removed from an issue
+category: InternalEventTracking
+action: g_project_management_issue_designs_removed
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_due_date_changed.yml b/config/events/g_project_management_issue_due_date_changed.yml
new file mode 100644
index 00000000000..140a4d62a10
--- /dev/null
+++ b/config/events/g_project_management_issue_due_date_changed.yml
@@ -0,0 +1,25 @@
+---
+description: An issue's due date was changed
+category: InternalEventTracking
+action: g_project_management_issue_due_date_changed
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_label_changed.yml b/config/events/g_project_management_issue_label_changed.yml
new file mode 100644
index 00000000000..1d99f46fb87
--- /dev/null
+++ b/config/events/g_project_management_issue_label_changed.yml
@@ -0,0 +1,25 @@
+---
+description: Label was changed on an issue
+category: InternalEventTracking
+action: g_project_management_issue_label_changed
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_locked.yml b/config/events/g_project_management_issue_locked.yml
new file mode 100644
index 00000000000..67e92907816
--- /dev/null
+++ b/config/events/g_project_management_issue_locked.yml
@@ -0,0 +1,25 @@
+---
+description: A issue was locked
+category: InternalEventTracking
+action: g_project_management_issue_locked
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_made_confidential.yml b/config/events/g_project_management_issue_made_confidential.yml
new file mode 100644
index 00000000000..e4bd84a69d7
--- /dev/null
+++ b/config/events/g_project_management_issue_made_confidential.yml
@@ -0,0 +1,25 @@
+---
+description: An issue was made confidential
+category: InternalEventTracking
+action: g_project_management_issue_made_confidential
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_made_visible.yml b/config/events/g_project_management_issue_made_visible.yml
new file mode 100644
index 00000000000..682cf4670bb
--- /dev/null
+++ b/config/events/g_project_management_issue_made_visible.yml
@@ -0,0 +1,25 @@
+---
+description: An issue was made visible
+category: InternalEventTracking
+action: g_project_management_issue_made_visible
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_marked_as_duplicate.yml b/config/events/g_project_management_issue_marked_as_duplicate.yml
new file mode 100644
index 00000000000..81417d68f4b
--- /dev/null
+++ b/config/events/g_project_management_issue_marked_as_duplicate.yml
@@ -0,0 +1,25 @@
+---
+description: A issue was marked as a duplicate
+category: InternalEventTracking
+action: g_project_management_issue_marked_as_duplicate
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_milestone_changed.yml b/config/events/g_project_management_issue_milestone_changed.yml
new file mode 100644
index 00000000000..9c7fa8749f8
--- /dev/null
+++ b/config/events/g_project_management_issue_milestone_changed.yml
@@ -0,0 +1,25 @@
+---
+description: Milestone changed on an issue
+category: InternalEventTracking
+action: g_project_management_issue_milestone_changed
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_moved.yml b/config/events/g_project_management_issue_moved.yml
new file mode 100644
index 00000000000..f07d80fc273
--- /dev/null
+++ b/config/events/g_project_management_issue_moved.yml
@@ -0,0 +1,25 @@
+---
+description: An issue was moved to another project
+category: InternalEventTracking
+action: g_project_management_issue_moved
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_related.yml b/config/events/g_project_management_issue_related.yml
new file mode 100644
index 00000000000..89ddef2e229
--- /dev/null
+++ b/config/events/g_project_management_issue_related.yml
@@ -0,0 +1,25 @@
+---
+description: An issue was marked as related to another issue
+category: InternalEventTracking
+action: g_project_management_issue_related
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_reopened.yml b/config/events/g_project_management_issue_reopened.yml
new file mode 100644
index 00000000000..ce5d3f23046
--- /dev/null
+++ b/config/events/g_project_management_issue_reopened.yml
@@ -0,0 +1,25 @@
+---
+description: An issue was reopened
+category: InternalEventTracking
+action: g_project_management_issue_reopened
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_time_estimate_changed.yml b/config/events/g_project_management_issue_time_estimate_changed.yml
new file mode 100644
index 00000000000..f90254faa63
--- /dev/null
+++ b/config/events/g_project_management_issue_time_estimate_changed.yml
@@ -0,0 +1,25 @@
+---
+description: An issue's time estimate was changed
+category: InternalEventTracking
+action: g_project_management_issue_time_estimate_changed
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_time_spent_changed.yml b/config/events/g_project_management_issue_time_spent_changed.yml
new file mode 100644
index 00000000000..9a8b9d8dd1e
--- /dev/null
+++ b/config/events/g_project_management_issue_time_spent_changed.yml
@@ -0,0 +1,25 @@
+---
+description: An issue's time spent was changed
+category: InternalEventTracking
+action: g_project_management_issue_time_spent_changed
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_title_changed.yml b/config/events/g_project_management_issue_title_changed.yml
new file mode 100644
index 00000000000..c071ccf2100
--- /dev/null
+++ b/config/events/g_project_management_issue_title_changed.yml
@@ -0,0 +1,25 @@
+---
+description: Title of an issue changed
+category: InternalEventTracking
+action: g_project_management_issue_title_changed
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_unlocked.yml b/config/events/g_project_management_issue_unlocked.yml
new file mode 100644
index 00000000000..7d3b35e0ea2
--- /dev/null
+++ b/config/events/g_project_management_issue_unlocked.yml
@@ -0,0 +1,25 @@
+---
+description: An issue was unlocked
+category: InternalEventTracking
+action: g_project_management_issue_unlocked
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/g_project_management_issue_unrelated.yml b/config/events/g_project_management_issue_unrelated.yml
new file mode 100644
index 00000000000..369e6c9df07
--- /dev/null
+++ b/config/events/g_project_management_issue_unrelated.yml
@@ -0,0 +1,25 @@
+---
+description: An issue was unrelated to another issue
+category: InternalEventTracking
+action: g_project_management_issue_unrelated
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: plan
+product_group: project_management
+milestone: "16.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131847
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 428c0830d83..3f9638093ae 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -309,7 +309,7 @@ Returns [`EpicList`](#epiclist).
### `Query.explainVulnerabilityPrompt`
-Explain This Vulnerability Prompt for a specified Vulnerability.
+GitLab Duo Vulnerability summary prompt for a specified vulnerability.
WARNING:
**Introduced** in 16.2.
@@ -1782,7 +1782,7 @@ Input type: `CiAiGenerateConfigInput`
| ---- | ---- | ----------- |
| <a id="mutationciaigenerateconfigclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationciaigenerateconfigerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
-| <a id="mutationciaigenerateconfigusermessage"></a>`userMessage` | [`DeprecatedAiMessage`](#deprecatedaimessage) | User chat message. |
+| <a id="mutationciaigenerateconfigusermessage"></a>`userMessage` | [`DeprecatedAiMessage`](#deprecatedaimessage) | User Chat message. |
### `Mutation.ciJobTokenScopeAddProject`
@@ -4784,7 +4784,7 @@ Input type: `JobRetryInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationjobretryclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
-| <a id="mutationjobretryid"></a>`id` | [`CiBuildID!`](#cibuildid) | ID of the job to mutate. |
+| <a id="mutationjobretryid"></a>`id` | [`CiProcessableID!`](#ciprocessableid) | ID of the job to mutate. |
| <a id="mutationjobretryvariables"></a>`variables` | [`[CiVariableInput!]`](#civariableinput) | Variables to use when retrying a manual job. |
#### Fields
@@ -29464,6 +29464,12 @@ A `CiPipelineScheduleVariableID` is a global ID. It is encoded as a string.
An example `CiPipelineScheduleVariableID` is: `"gid://gitlab/Ci::PipelineScheduleVariable/1"`.
+### `CiProcessableID`
+
+A `CiProcessableID` is a global ID. It is encoded as a string.
+
+An example `CiProcessableID` is: `"gid://gitlab/Ci::Processable/1"`.
+
### `CiRunnerID`
A `CiRunnerID` is a global ID. It is encoded as a string.
diff --git a/doc/architecture/blueprints/email_ingestion/index.md b/doc/architecture/blueprints/email_ingestion/index.md
new file mode 100644
index 00000000000..efaa5e6cf4c
--- /dev/null
+++ b/doc/architecture/blueprints/email_ingestion/index.md
@@ -0,0 +1,169 @@
+---
+status: proposed
+creation-date: "2023-06-05"
+authors: [ "@msaleiko" ]
+coach: "@stanhu"
+approvers: [ "@kbychu", "@bmarnane" ]
+owning-stage: "~devops::monitor"
+participating-stages: [ "~group::incubation" ]
+---
+
+<!-- vale gitlab.FutureTense = NO -->
+<!-- vale gitlab.CurrentStatus = NO -->
+
+# Replace `mail_room` email ingestion with scheduled Sidekiq jobs
+
+## Summary
+
+GitLab users can submit new issues and comments via email. Administrators configure special mailboxes that GitLab polls on a regular basis and fetches new unread emails. Based on the slug and a hash in the sub-addressing part of the email address, we determine whether this email will file an issue, add a Service Desk issue, or a comment to an existing issue.
+
+Right now emails are ingested by a separate process called `mail_room`. We would like to stop ingesting emails via `mail_room` and instead use scheduled Sidekiq jobs to do this directly inside GitLab.
+
+This lays out the foundation for [custom email address ingestion for Service Desk](https://gitlab.com/gitlab-org/gitlab/-/issues/329990), detailed health logging and makes it easier to integrate other service provider adapters (for example Gmail via API). We will also reduce the infrastructure setup and maintenance costs for customers on self-managed and make it easier for team members to work with email ingestion in GDK.
+
+## Glossary
+
+- Email ingestion: Reading emails from a mailbox via IMAP or an API and forwarding it for processing (for example create an issue or add a comment)
+- Sub-addressing: An email address consist of a local part (everything before `@`) and a domain part. With email sub-addressing you can create unique variations of an email address by adding a `+` symbol followed by any text to the local part. You can use these sub-addresses to filter, categorize or distinguish between them as all these emails will be delivered to the same mailbox. For example `user+subaddress@example.com` and `user+1@example.com` and sub-addresses for `user@example.com`.
+- `mail_room`: [An executable script](https://gitlab.com/gitlab-org/ruby/gems/gitlab-mail_room) that spawns a new process for each configured mailbox, reads new emails on a regular basis and forwards the emails to a processing unit.
+- [`incoming_email`](../../../administration/incoming_email.md): An email address that is used for adding comments and issues via email. When you reply on a GitLab notification of an issue comment, this response email will go to the configured `incoming_email` mailbox, read via `mail_room` and processed by GitLab. You can also use this address as a Service Desk email address. The configuration is per instance and needs full IMAP or Microsoft Graph API credentials to access the mailbox.
+- [`service_desk_email`](../../../user/project/service_desk/configure.md#use-an-additional-service-desk-alias-email): Additional alias email address that is only used for Service Desk. You can also use an address generated from `incoming_email` to create Service Desk issues.
+- `delivery_method`: Administrators can define how `mail_room` forwards fetched emails to GitLab. The legacy and now deprecated approach is called `sidekiq`, which directly adds a new job to the Redis queue. The current and recommended way is called `webhook`, which sends a POST request to an internal GitLab API endpoint. This endpoint then adds a new job using the full framework for compressing job data etc. The downside is, that `mail_room` and GitLab need a shared key file, which might be challenging to distribute in large setups.
+
+## Motivation
+
+The current implementation lacks scalability and requires significant infrastructure maintenance. Additionally, there is a lack of [proper observability for configuration errors](https://gitlab.com/gitlab-org/gitlab/-/issues/384530) and [overall system health](https://gitlab.com/groups/gitlab-org/-/epics/9407). Furthermore, [setting up and providing support for multi-node Linux package (Omnibus) installations](https://gitlab.com/gitlab-org/gitlab/-/issues/391859) is challenging, and periodic email ingestion issues necessitate reactive support.
+
+Because we are using a fork of the `mail_room` gem ([`gitlab-mail_room`](https://gitlab.com/gitlab-org/ruby/gems/gitlab-mail_room)), which contains some GitLab specific features that won't be ported upstream, we have a noteable maintenance overhead.
+
+The [Service Desk Single-Engineer-Group (SEG)](https://about.gitlab.com/handbook/engineering/incubation/service-desk/) started work on [customizable email addresses for Service Desk](https://gitlab.com/gitlab-org/gitlab/-/issues/329990) and [released the first iteration in beta in `16.4`](https://about.gitlab.com/releases/2023/09/22/gitlab-16-4-released/#custom-email-address-for-service-desk). As a [MVC we introduced a `Forwarding & SMTP` mode](https://gitlab.com/gitlab-org/gitlab/-/issues/329990#note_1201344150) where administrators set up email forwarding from their custom email address to the projects' `incoming_mail` email address. They also provide SMTP credentials so GitLab can send emails from the custom email address on their behalf. We don't need any additional email ingestion other than the existing mechanics for this approach to work.
+
+As a second iteration we'd like to add Microsoft Graph support for custom email addresses for Service Desk as well. Therefore we need a way to ingest more than the system defined two addresses. We will explore a solution path for Microsoft Graph support where privileged users can connect a custom email account and we can [receive messages via a Microsoft Graph webhook (`Outlook message`)](https://learn.microsoft.com/en-us/graph/webhooks#supported-resources). GitLab would need a public endpoint to receive updates on emails. That might not work for Self-managed instances, so we'll need direct email ingestion for Microsoft customers as well. But using the webhook approach could improve performance and efficiency for GitLab SaaS where we potentially have thousands of mailboxes to poll.
+
+### Goals
+
+Our goals for this initiative are to enhance the scalability of email ingestion and slim down the infrastructure significantly.
+
+1. This consolidation will eliminate the need for setup for the separate process and pave the way for future initiatives, including direct custom email address ingestion (IMAP & Microsoft Graph), [improved health monitoring](https://gitlab.com/groups/gitlab-org/-/epics/9407), [data retention (preserving originals)](https://gitlab.com/groups/gitlab-org/-/epics/10521), and [enhanced processing of attachments within email size limits](https://gitlab.com/gitlab-org/gitlab/-/issues/406668).
+1. Make it easier for team members to develop features with email ingestion. [Right now it needs several manual steps.](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/howto/service_desk_mail_room.md)
+
+### Non-Goals
+
+This blueprint does not aim to lay out implementation details for all the listed future initiatives. But it will be the foundation for upcoming features (customizable Service Desk email address IMAP/Microsoft Graph, health checks etc.).
+
+We don't include other ingestion methods. We focus on delivering the current set: IMAP and Microsoft Graph API for `incoming_email` and `service_desk_email`.
+
+## Current setup
+
+Administrators configure settings (credentials and delivery method) for email mailboxes (for [`incoming_email`](../../../administration/incoming_email.md) and [`service_desk_email`](../../../user/project/service_desk/configure.md#use-an-additional-service-desk-alias-email)) in `gitlab.rb` configuration file. After each change GitLab needs to be reconfigured and restarted to apply the new settings.
+
+We use the separate process `mail_room` to ingest emails from those mailboxes. `mail_room` spawns a thread for each configured mailbox and polls those mailboxes every minute. In the meantime the threads are idle. `mail_room` reads a configuration file that is generated from the settings in `gitlab.rb`.
+
+`mail_room` can connect via IMAP and Microsoft Graph, fetch unread emails, and mark them as read or deleted (based on settings). It takes an email and distributes it to its destination via one of the two delivery methods.
+
+### `webhook` delivery method (recommended)
+
+The `webhook` delivery method is the recommended way to move ingested emails from `mail_room` to GitLab. `mail_room` posts the email body and metadata to an internal API endpoint `/api/v4/internal/mail_room`, that selects the correct handler worker and schedules it for execution.
+
+```mermaid
+flowchart TB
+ User --Sends email--> provider[(Email provider mailbox)]
+ mail_room --Fetch unread emails via IMAP or Microsoft Graph API--> provider
+ mail_room --HTTP POST--> api
+ api --adds job for email--> create
+
+ subgraph mail_room_process[mail_room]
+ mail_room[mail_room thread]
+ end
+
+ subgraph GitLab
+ api[Internal API endpoint]
+ create["Sidekiq email handler job
+ that create issue/note based
+ on email address"]
+ end
+```
+
+### `sidekiq` delivery method (deprecated since 16.0)
+
+The `sidekiq` delivery method adds the email body and metadata directly to the Redis queue that Sidekiq uses to manage jobs. It has been [deprecated in 16.0](../../../update/deprecations.md#sidekiq-delivery-method-for-incoming_email-and-service_desk_email-is-deprecated) because there is a hard coupling between the delivery method and the Redis configuration. Moreover we cannot use Sidekiq framework optimizations such as job payload compression.
+
+```mermaid
+flowchart TB
+ User --Sends email--> provider[(Email provider mailbox)]
+ mail_room --Fetch unread emails via IMAP or Microsoft Graph API--> provider
+
+ mail_room --directly writes to Redis queue, which schedules a handler job--> redis[Redis queue]
+ redis --Sidekiq takes job from the queue and executes it--> create
+
+ subgraph mail_room_process[mail_room]
+ mail_room[mail_room thread]
+ end
+
+ subgraph GitLab
+ create["Sidekiq email handler job
+ that create issue/note based
+ on email address"]
+ end
+```
+
+## Proposal
+
+**Use Sidekiq jobs to poll mailboxes on a regular basis (every minute, maybe configurable in the future).
+Remove all other legacy email ingestion infrastructure.**
+
+```mermaid
+flowchart TB
+ User --Sends email--> provider[(Email provider mailbox)]
+ ingestion --Fetch unread emails via IMAP or Microsoft Graph API--> provider
+ controller --Triggers a job for each mailbox--> ingestion
+ ingestion --Adds a job for each fetched email--> create
+
+ subgraph GitLab
+ controller[Scheduled Sidekiq ingestion controller job]
+ ingestion[Sidekiq mailbox ingestion job]
+ create["Existing Sidekiq email handler jobs
+ that create issue/note based
+ on email address"]
+ end
+```
+
+1. Use a `controller` job that is scheduled every minute or every two minutes. This job adds one job for each configured mailbox (`incoming_email` and `service_desk_email`).
+1. The concrete `ingestion` job polls a mailbox (IMAP or Microsoft Graph), downloads unread emails and adds one job for each email that processes the email. We decide based on the used `To` email address which email handler should be used.
+1. The `existing email handler` jobs try to create an issue, a Service Desk issue or a note on an existing issue/merge request. These handlers are also used by the legacy email ingestion via `mail_room`.
+
+### Sidekiq jobs and job payload size optimizations
+
+We implemented a size limit for Sidekiq jobs and email job payloads (especially emails with attachments) are likely to pass that bar. We should experiment with the idea of handling email processing directly in the Sidekiq mailbox ingestion job. We could use an `ops` feature flag to switch between this mode and a Sidekiq job for each email.
+
+We'd also like to explore a solution path where we only fetch the message ids and then download the complete messages in child jobs (filter by `UID` range for example). For example we poll a mailbox and fetch a list of message ids. Then we create a new job for every 25 (or n) emails that takes the message ids or the range as an argument. These jobs will then download the entire messages and synchronously add issues or replies. If the number of emails is below 25, we could even handle the emails directly in the current job to save resources. This will allow us to eliminate the job payload size as the limiting factor for the size of emails. The disadvantage is that we need to make two calls to the IMAP server instead of one (n+1).
+
+## Execution plan
+
+1. Add deprecation for `mail_room` email ingestion.
+1. Strip out connection-specific logic from [`gitlab-mail_room` gem](https://gitlab.com/gitlab-org/ruby/gems/gitlab-mail_room), into a new separate gem. `mail_room` and other clients could use our work here. Right now we support IMAP and Microsoft Graph API connections.
+1. Add new jobs (set idempotency and de-duplication flags to avoid a huge backlog of jobs if Sidekiq isn't running).
+1. Add a setting (`gitlab.rb`) that enables email ingestion with Sidekiq jobs inside GitLab. We need to set `mailroom['enabled'] = false` in `gitlab.rb` to disable `mail_room` email ingestion. Maybe additionally add a feature flag.
+1. Use on `gitlab.com` before general availability, but allow self-managed to try it out in `beta`.
+1. Once rolled out in general availability and when removal has been scheduled, remove the dependency to `gitlab-mail_room` entirely, remove the internal API endpoint `api/internal/mail_room`, remove `mail_room.yml` dynamically generated static configuration file for `mail_room` and other configuration and binaries.
+
+## Change management
+
+We decided to [deprecate the `sidekiq` delivery method for `mail_room` in GitLab 16.0](../../../update/deprecations.md#sidekiq-delivery-method-for-incoming_email-and-service_desk_email-is-deprecated) and scheduled it for removal in GitLab 17.0.
+We can only remove the `sidekiq` delivery method after this blueprint has been implemented and our customers can use the new email ingestion in general availability.
+
+We should then schedule `mail_room` for removal (GitLab 17.0 or later). This will be a breaking change. We could make the new email ingestion the default beforehand, so self-managed customers wouldn't need to take action.
+
+## Alternative Solutions
+
+### Do nothing
+
+The current setup limits us and only allows to fetch two email addresses. To publish Service Desk custom email addresses with IMAP or API integration we would need to deliver the same architecture as described above. Because of that we should act now and include general email ingestion for `incoming_email` and `service_desk_email` first and remove the infrastructure overhead.
+
+## Additional resources
+
+- [Draft issue for this blueprint](https://gitlab.com/gitlab-org/gitlab/-/issues/393157)
+
+## Timeline
+
+- 2023-XX-XX: The initial version of the blueprint has been merged.
diff --git a/doc/user/ai_features.md b/doc/user/ai_features.md
index feea06666dc..888c70854e5 100644
--- a/doc/user/ai_features.md
+++ b/doc/user/ai_features.md
@@ -32,7 +32,7 @@ The [Generally Available](../policy/experiment-beta-support.md#generally-availab
[Experiment features](../policy/experiment-beta-support.md#experiment) and [Beta features](../policy/experiment-beta-support.md#beta) (besides Code Suggestions) on SaaS must be enabled by a user who has the Owner role in the group. Their usage is subject to the [Testing Terms of Use](https://about.gitlab.com/handbook/legal/testing-agreement/).
-In addition, all features built on large language models (LLM) from Google, Anthropic or OpenAI require that [third-party AI features are enabled](group/manage.md#enable-third-party-ai-features) (which they are by default). The table above shows which features are built on which LLM. To disable AI features powered by third-party APIs, clear this setting.
+In addition, all features built on large language models (LLM) from Google, Anthropic or OpenAI (besides Code Suggestions) require that [third-party AI features are enabled](group/manage.md#enable-third-party-ai-features) (which they are by default). The table above shows which features are built on which LLM. To disable AI features powered by third-party APIs, clear this setting.
Code Suggestions currently has its own settings:
diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md
index 9d4a151cc53..ae74b534e02 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -199,7 +199,7 @@ You can also mark an [issue as confidential](../project/issues/confidential_issu
## Show only comments
In discussions with many comments, filter the discussion to show only comments or history of
-changes (system notes). System notes include changes to the description, mentions in other GitLab
+changes ([system notes](../project/system_notes.md)). System notes include changes to the description, mentions in other GitLab
objects, or changes to labels, assignees, and the milestone.
GitLab saves your preference, and applies it to every issue, merge request, or epic you view.
diff --git a/doc/user/project/system_notes.md b/doc/user/project/system_notes.md
index 661f10290c6..73509846990 100644
--- a/doc/user/project/system_notes.md
+++ b/doc/user/project/system_notes.md
@@ -51,6 +51,15 @@ The filtering options are:
1. Go to **Activity**.
1. For **Sort or filter**, select **Show all activity**.
+## Privacy considerations
+
+You can see only the system notes linked to objects you can access.
+
+For example, if someone mentions your issue 111 in an issue in their private project:
+
+- The project members see the following note in issue 111: `Alex Garcia mentioned in agarcia/private-project#222`.
+- Non-members of the project can't see the note at all.
+
## Related topics
- [Notes API](../../api/notes.md)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index c42a6d27f22..06edc036042 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1812,6 +1812,9 @@ msgstr ""
msgid "A new Release %{tag} for %{name} was published. Visit the Releases page to read more about it:"
msgstr ""
+msgid "A new application has been created"
+msgstr ""
+
msgid "A new email address has been added to your GitLab account: %{email}"
msgstr ""
@@ -4919,6 +4922,9 @@ msgstr ""
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
+msgid "An application has been authorized"
+msgstr ""
+
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
@@ -31893,6 +31899,12 @@ msgstr ""
msgid "Notify|A new GPG key was added to your account:"
msgstr ""
+msgid "Notify|A new OAuth 2 application has been authorized. If you did not expect this, check the authorized OAuth 2 applications %{docs_link}."
+msgstr ""
+
+msgid "Notify|A new OAuth 2 application has been created. If you did not create this application, check the OAuth 2 applications %{docs_link}."
+msgstr ""
+
msgid "Notify|A remote mirror update has failed."
msgstr ""
@@ -55932,6 +55944,9 @@ msgstr ""
msgid "disabled"
msgstr ""
+msgid "documentation"
+msgstr ""
+
msgid "does not exist"
msgstr ""
diff --git a/spec/frontend/boards/board_list_helper.js b/spec/frontend/boards/board_list_helper.js
index 7367b34c4df..5bafd9a8d0e 100644
--- a/spec/frontend/boards/board_list_helper.js
+++ b/spec/frontend/boards/board_list_helper.js
@@ -122,5 +122,7 @@ export default function createComponent({
},
});
+ jest.spyOn(store, 'dispatch').mockImplementation(() => {});
+
return component;
}
diff --git a/spec/frontend/boards/board_list_spec.js b/spec/frontend/boards/board_list_spec.js
index e0a110678b1..30bb4fba4e3 100644
--- a/spec/frontend/boards/board_list_spec.js
+++ b/spec/frontend/boards/board_list_spec.js
@@ -202,8 +202,6 @@ describe('Board list component', () => {
describe('handleDragOnEnd', () => {
beforeEach(() => {
- jest.spyOn(wrapper.vm, 'moveItem').mockImplementation(() => {});
-
startDrag();
});
diff --git a/spec/mailers/emails/profile_spec.rb b/spec/mailers/emails/profile_spec.rb
index 4816e88a311..3b0dfaff2cb 100644
--- a/spec/mailers/emails/profile_spec.rb
+++ b/spec/mailers/emails/profile_spec.rb
@@ -124,6 +124,46 @@ RSpec.describe Emails::Profile, feature_category: :user_profile do
end
end
+ describe 'new application has been created' do
+ let_it_be(:user) { create(:user) }
+
+ context 'when valid' do
+ subject { Notify.application_created_email(user) }
+
+ it_behaves_like 'an email sent from GitLab'
+ it_behaves_like 'it should not have Gmail Actions links'
+ it_behaves_like 'a user cannot unsubscribe through footer link'
+
+ it 'is sent to the user' do
+ is_expected.to deliver_to user.email
+ end
+
+ it 'has the correct subject' do
+ is_expected.to have_subject /^A new application has been created$/i
+ end
+ end
+ end
+
+ describe 'An application has been authorized' do
+ let_it_be(:user) { create(:user) }
+
+ context 'when valid' do
+ subject { Notify.application_authorized_email(user) }
+
+ it_behaves_like 'an email sent from GitLab'
+ it_behaves_like 'it should not have Gmail Actions links'
+ it_behaves_like 'a user cannot unsubscribe through footer link'
+
+ it 'is sent to the user' do
+ is_expected.to deliver_to user.email
+ end
+
+ it 'has the correct subject' do
+ is_expected.to have_subject /^An application has been authorized$/i
+ end
+ end
+ end
+
describe 'user personal access token has been created' do
let_it_be(:user) { create(:user) }
let_it_be(:token) { create(:personal_access_token, user: user) }
diff --git a/spec/requests/api/graphql/mutations/ci/job/retry_spec.rb b/spec/requests/api/graphql/mutations/ci/job/retry_spec.rb
index 4114c77491b..82988854719 100644
--- a/spec/requests/api/graphql/mutations/ci/job/retry_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/job/retry_spec.rb
@@ -37,14 +37,30 @@ RSpec.describe 'JobRetry', feature_category: :continuous_integration do
expect(graphql_errors).not_to be_empty
end
- it 'retries a job' do
- post_graphql_mutation(mutation, current_user: user)
+ context 'when the job is a Ci::Build' do
+ it 'retries the build' do
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ new_job_id = GitlabSchema.object_from_id(mutation_response['job']['id']).sync.id
+
+ new_job = ::Ci::Build.find(new_job_id)
+ expect(new_job).not_to be_retried
+ end
+ end
+
+ context 'when the job is a Ci::Bridge' do
+ let(:job) { create(:ci_bridge, :success, pipeline: pipeline, name: 'puente') }
- expect(response).to have_gitlab_http_status(:success)
- new_job_id = GitlabSchema.object_from_id(mutation_response['job']['id']).sync.id
+ it 'retries the bridge' do
+ post_graphql_mutation(mutation, current_user: user)
- new_job = ::Ci::Build.find(new_job_id)
- expect(new_job).not_to be_retried
+ expect(response).to have_gitlab_http_status(:success)
+ new_job_id = GitlabSchema.object_from_id(mutation_response['job']['id']).sync.id
+
+ new_job = ::Ci::Bridge.find(new_job_id)
+ expect(new_job).not_to be_retried
+ end
end
context 'when given CI variables' do
diff --git a/spec/services/applications/create_service_spec.rb b/spec/services/applications/create_service_spec.rb
index 8b8beb057a9..92e63a24b2a 100644
--- a/spec/services/applications/create_service_spec.rb
+++ b/spec/services/applications/create_service_spec.rb
@@ -12,7 +12,13 @@ RSpec.describe ::Applications::CreateService do
context 'when scopes are present' do
let(:params) { attributes_for(:application, scopes: ['read_user']) }
- it { expect { subject.execute(test_request) }.to change { Doorkeeper::Application.count }.by(1) }
+ it 'receive notification and change application count' do
+ notification_service = instance_double(NotificationService)
+ allow(NotificationService).to receive(:new).and_return(notification_service)
+
+ expect(notification_service).to receive(:application_created).with(user)
+ expect { subject.execute(test_request) }.to change { Doorkeeper::Application.count }.by(1)
+ end
end
context 'when scopes are missing' do
diff --git a/spec/services/authorizations/notification_service_spec.rb b/spec/services/authorizations/notification_service_spec.rb
new file mode 100644
index 00000000000..eae29b193e3
--- /dev/null
+++ b/spec/services/authorizations/notification_service_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe ::Authorizations::NotificationService, feature_category: :integrations do
+ include TestRequestHelpers
+
+ let(:user) { create(:user) }
+
+ subject { described_class.new(user) }
+
+ it 'receive notification' do
+ notification_service = instance_double(NotificationService)
+ allow(NotificationService).to receive(:new).and_return(notification_service)
+
+ expect(notification_service).to receive(:application_authorized).with(user)
+ subject.execute
+ end
+end