Remove the current targeted messages feature: code removal

Summary

Remove the current targeted messages code from GitLab.com: the admin UI, the billing page banner, the GraphQL field, the models, the services and the entry points. Growth will redesign targeted messages as part of the Personalization Engine work, starting with the audience layer and then the delivery layer.

Decided in !263237 (comment 4096009314).

This issue is release M of the removal. The leftovers are split out because they span several milestones:

Both are blocked until the changes here are released.

Problem

The feature is only half built, and we don't plan to build on its current shape:

  • Audiences come from an uploaded CSV of namespace IDs.
  • The banner shows only hardcoded Premium promo copy, and only on the group Billing page.
  • It's behind the targeted_messages_admin_ui gitlab_com_derisk flag and the targeted_messages SaaS feature. It still has to be maintained.

Scope

Remove in small MRs, in this order where it matters. Finish each part with grep -ri targeted_message to catch leftovers.

1. Frontend and entry points

  • Billing banner: ee/app/assets/javascripts/targeted_message_banner/, its init and mount point (#js-targeted-message-banner) in ee/app/assets/javascripts/pages/groups/billings/index.js and the billing view, and ee/spec/frontend/targeted_messages/.
  • Admin form: app/assets/javascripts/admin/targeted_messages/, ee/app/assets/javascripts/pages/admin/targeted_messages/ (including the two vue3_migration.yml files) and ee/spec/frontend/admin/targeted_messages/.
  • Admin views: ee/app/views/admin/targeted_messages/.
  • Admin sidebar: delete ee/lib/ee/sidebars/admin/menus/messages_menu.rb and its spec. Deleting only the targeted item would leave the menu in a wrong state. The FOSS MessagesMenu already links straight to broadcast messages.
  • Routes: ee/config/routes/instance_admin.rb, ee/config/routes/user.rb.
  • Regenerate the path helpers ee/app/assets/javascripts/lib/utils/path_helpers/user.js and instance_admin.js.

2. Backend

  • Controllers and specs: Admin::TargetedMessagesController, Users::TargetedMessageDismissalsController.
  • Services and specs: Notifications::TargetedMessages::{BaseService,CreateService,UpdateService,NamespaceIdsBuilder}.
  • Notifications::TargetedMessagePolicy and its spec.
  • Models and specs: Notifications::TargetedMessage, TargetedMessageNamespace, TargetedMessageDismissal, and their factories in ee/spec/factories/notifications/.
  • User association: remove targeted_message_dismissals from EE::Users::DependentAssociations and its spec. Before removing it, confirm the foreign key from targeted_message_dismissals to users has ON DELETE CASCADE. Otherwise user deletion breaks until the table is dropped.
  • Worker: stop enqueuing it (the controller removal does this). Make Notifications::TargetedMessages::DestroyWorker#perform a no-op that keeps its argument, per step 1 of worker removal. Keep the class. #637388 deletes it.
  • Config: ee/config/saas_features/targeted_messages.yml and ee/config/feature_flags/gitlab_com_derisk/targeted_messages_admin_ui.yml, plus any push_frontend_feature_flag and Feature.enabled? references.
  • Rubocop todo: .rubocop_todo/rspec/rails_controller_testing.yml.
  • Strings: regenerate locale/gitlab.pot (TargetedMessages| strings).

3. GraphQL

  • Remove Namespace.targetedMessages (ee/app/graphql/ee/types/namespace_type.rb), Types::Notifications::TargetedMessageType and Resolvers::Notifications::TargetedMessageResolver, with their specs. The field is marked experiment (18.7), so no deprecation period is needed.
  • Remove type:TargetedMessage from config/authz/graphql/authorization_todo.txt.
  • Regenerate the GraphQL reference docs.

4. Out of scope (separate issues)

  • Worker removal: #637388. The queue files config/sidekiq_queues.yml and ee/app/workers/all_queues.yml change there.
  • Database removal: #637389. The allowlists spec/support/database/retention-policy-missing-allowlist.yml and spec/support/database/destroy_service_foreign_keys_todo.yml and the db/docs files change there.

Close or update the related open work: #634504 and the targeted messages epic. The MRs !263237 (closed) and !263236 (closed) are already closed.

What we learned (input for the design doc)

Requested in !263237 (comment 4096009314). @swiskow, please add the details of the 2 October incident and anything else that's missing.

  • 2 October. Premium and Ultimate customers saw a date meant only for Free (from @swiskow). This is why plan targeting was requested.
  • List audiences. Namespace IDs are written synchronously in the admin request. For the POC, that's why uploads are capped at 50,000. A list is also a snapshot. Larger or changing cohorts need background processing or rules-based audiences.
  • Dismissals. Stored per user, namespace and message, so dismissing in one group doesn't hide the message in another. The endpoint doesn't check that the message targets the namespace or that the user is a member, and it has no rate limit.
  • Schedule. The resolver doesn't filter on starts_at/ends_at (fixed in !263236 (closed), unmerged).
  • Rules. Each new filter interacts with the others and needs its own performance check. Design the audience model up front instead of adding filters one at a time.
  • Delivery. Planned per-message page placement (#528871), async loading (#535457 (closed)), and up to two CTAs with named routes (#527968).
  • Interim. Use a one-off dismissible banner behind a feature flag that checks namespace.plan_tier (!263237 (comment 4053062002)).
Edited by 🤖 GitLab Bot 🤖