Enqueue queues a notification message for later delivery. Messages will be dequeued by a notifier later and dispatched. Returns the IDs of successfully enqueued messages, if any.
(ctx context.Context, userID, templateID uuid.UUID, labels map[string]string, data map[string]any, createdBy string, targets ...uuid.UUID)
| 81 | // Messages will be dequeued by a notifier later and dispatched. |
| 82 | // Returns the IDs of successfully enqueued messages, if any. |
| 83 | func (s *StoreEnqueuer) EnqueueWithData(ctx context.Context, userID, templateID uuid.UUID, labels map[string]string, data map[string]any, createdBy string, targets ...uuid.UUID) ([]uuid.UUID, error) { |
| 84 | metadata, err := s.store.FetchNewMessageMetadata(ctx, database.FetchNewMessageMetadataParams{ |
| 85 | UserID: userID, |
| 86 | NotificationTemplateID: templateID, |
| 87 | }) |
| 88 | if err != nil { |
| 89 | s.log.Warn(ctx, "failed to fetch message metadata", slog.F("template_id", templateID), slog.F("user_id", userID), slog.Error(err)) |
| 90 | return nil, xerrors.Errorf("new message metadata: %w", err) |
| 91 | } |
| 92 | |
| 93 | payload, err := s.buildPayload(metadata, labels, data, targets) |
| 94 | if err != nil { |
| 95 | s.log.Warn(ctx, "failed to build payload", slog.F("template_id", templateID), slog.F("user_id", userID), slog.Error(err)) |
| 96 | return nil, xerrors.Errorf("enqueue notification (payload build): %w", err) |
| 97 | } |
| 98 | |
| 99 | input, err := json.Marshal(payload) |
| 100 | if err != nil { |
| 101 | return nil, xerrors.Errorf("failed encoding input labels: %w", err) |
| 102 | } |
| 103 | |
| 104 | methods := []database.NotificationMethod{} |
| 105 | if metadata.CustomMethod.Valid { |
| 106 | methods = append(methods, metadata.CustomMethod.NotificationMethod) |
| 107 | } else if s.defaultEnabled { |
| 108 | methods = append(methods, s.defaultMethod) |
| 109 | } |
| 110 | |
| 111 | // All the enqueued messages are enqueued both on the dispatch method set by the user (or default one) and the inbox. |
| 112 | // As the inbox is not configurable per the user and is always enabled, we always enqueue the message on the inbox. |
| 113 | // The logic is done here in order to have two completely separated processing and retries are handled separately. |
| 114 | if !slices.Contains(methods, database.NotificationMethodInbox) && s.inboxEnabled { |
| 115 | methods = append(methods, database.NotificationMethodInbox) |
| 116 | } |
| 117 | |
| 118 | uuids := make([]uuid.UUID, 0, 2) |
| 119 | for _, method := range methods { |
| 120 | // TODO(DanielleMaywood): |
| 121 | // We should have a more permanent solution in the future, but for now this will work. |
| 122 | // We do not want password reset notifications to end up in Coder Inbox. |
| 123 | if method == database.NotificationMethodInbox && templateID == TemplateUserRequestedOneTimePasscode { |
| 124 | continue |
| 125 | } |
| 126 | |
| 127 | id := uuid.New() |
| 128 | err = s.store.EnqueueNotificationMessage(ctx, database.EnqueueNotificationMessageParams{ |
| 129 | ID: id, |
| 130 | UserID: userID, |
| 131 | NotificationTemplateID: templateID, |
| 132 | Method: method, |
| 133 | Payload: input, |
| 134 | Targets: targets, |
| 135 | CreatedBy: createdBy, |
| 136 | CreatedAt: dbtime.Time(s.clock.Now().UTC()), |
| 137 | }) |
| 138 | if err != nil { |
| 139 | // We have a trigger on the notification_messages table named `inhibit_enqueue_if_disabled` which prevents messages |
| 140 | // from being enqueued if the user has disabled them via notification_preferences. The trigger will fail the insertion |