feat!: due date reminder notifications

This commit is contained in:
Jordan Knott
2021-11-17 17:11:28 -06:00
parent 0d00fc7518
commit 886b2763ee
32 changed files with 1244 additions and 287 deletions

View File

@ -277,6 +277,7 @@ type ComplexityRoot struct {
DuplicateTaskGroup func(childComplexity int, input DuplicateTaskGroup) int
InviteProjectMembers func(childComplexity int, input InviteProjectMembers) int
LogoutUser func(childComplexity int, input LogoutUser) int
NotificationMarkAllRead func(childComplexity int) int
NotificationToggleRead func(childComplexity int, input NotificationToggleReadInput) int
RemoveTaskLabel func(childComplexity int, input *RemoveTaskLabelInput) int
SetTaskChecklistItemComplete func(childComplexity int, input SetTaskChecklistItemComplete) int
@ -333,6 +334,10 @@ type ComplexityRoot struct {
Value func(childComplexity int) int
}
NotificationMarkAllAsReadResult struct {
Success func(childComplexity int) int
}
Notified struct {
ID func(childComplexity int) int
Notification func(childComplexity int) int
@ -617,6 +622,7 @@ type LabelColorResolver interface {
}
type MutationResolver interface {
NotificationToggleRead(ctx context.Context, input NotificationToggleReadInput) (*Notified, error)
NotificationMarkAllRead(ctx context.Context) (*NotificationMarkAllAsReadResult, error)
CreateProjectLabel(ctx context.Context, input NewProjectLabel) (*db.ProjectLabel, error)
DeleteProjectLabel(ctx context.Context, input DeleteProjectLabel) (*db.ProjectLabel, error)
UpdateProjectLabel(ctx context.Context, input UpdateProjectLabel) (*db.ProjectLabel, error)
@ -1755,6 +1761,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Mutation.LogoutUser(childComplexity, args["input"].(LogoutUser)), true
case "Mutation.notificationMarkAllRead":
if e.complexity.Mutation.NotificationMarkAllRead == nil {
break
}
return e.complexity.Mutation.NotificationMarkAllRead(childComplexity), true
case "Mutation.notificationToggleRead":
if e.complexity.Mutation.NotificationToggleRead == nil {
break
@ -2199,6 +2212,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.NotificationData.Value(childComplexity), true
case "NotificationMarkAllAsReadResult.success":
if e.complexity.NotificationMarkAllAsReadResult.Success == nil {
break
}
return e.complexity.NotificationMarkAllAsReadResult.Success(childComplexity), true
case "Notified.id":
if e.complexity.Notified.ID == nil {
break
@ -3417,6 +3437,10 @@ extend type Query {
extend type Mutation {
notificationToggleRead(input: NotificationToggleReadInput!): Notified!
notificationMarkAllRead: NotificationMarkAllAsReadResult!
}
type NotificationMarkAllAsReadResult {
success: Boolean!
}
type HasUnreadNotificationsResult {
@ -3452,6 +3476,7 @@ enum ActionType {
DUE_DATE_ADDED
DUE_DATE_REMOVED
DUE_DATE_CHANGED
DUE_DATE_REMINDER
TASK_ASSIGNED
TASK_MOVED
TASK_ARCHIVED
@ -8535,6 +8560,41 @@ func (ec *executionContext) _Mutation_notificationToggleRead(ctx context.Context
return ec.marshalNNotified2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐNotified(ctx, field.Selections, res)
}
func (ec *executionContext) _Mutation_notificationMarkAllRead(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Mutation",
Field: field,
Args: nil,
IsMethod: true,
IsResolver: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Mutation().NotificationMarkAllRead(rctx)
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(*NotificationMarkAllAsReadResult)
fc.Result = res
return ec.marshalNNotificationMarkAllAsReadResult2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐNotificationMarkAllAsReadResult(ctx, field.Selections, res)
}
func (ec *executionContext) _Mutation_createProjectLabel(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
@ -13267,6 +13327,41 @@ func (ec *executionContext) _NotificationData_value(ctx context.Context, field g
return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _NotificationMarkAllAsReadResult_success(ctx context.Context, field graphql.CollectedField, obj *NotificationMarkAllAsReadResult) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "NotificationMarkAllAsReadResult",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Success, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(bool)
fc.Result = res
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
}
func (ec *executionContext) _Notified_id(ctx context.Context, field graphql.CollectedField, obj *Notified) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
@ -23074,6 +23169,11 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
if out.Values[i] == graphql.Null {
invalids++
}
case "notificationMarkAllRead":
out.Values[i] = ec._Mutation_notificationMarkAllRead(ctx, field)
if out.Values[i] == graphql.Null {
invalids++
}
case "createProjectLabel":
out.Values[i] = ec._Mutation_createProjectLabel(ctx, field)
if out.Values[i] == graphql.Null {
@ -23580,6 +23680,33 @@ func (ec *executionContext) _NotificationData(ctx context.Context, sel ast.Selec
return out
}
var notificationMarkAllAsReadResultImplementors = []string{"NotificationMarkAllAsReadResult"}
func (ec *executionContext) _NotificationMarkAllAsReadResult(ctx context.Context, sel ast.SelectionSet, obj *NotificationMarkAllAsReadResult) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, notificationMarkAllAsReadResultImplementors)
out := graphql.NewFieldSet(fields)
var invalids uint32
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("NotificationMarkAllAsReadResult")
case "success":
out.Values[i] = ec._NotificationMarkAllAsReadResult_success(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch()
if invalids > 0 {
return graphql.Null
}
return out
}
var notifiedImplementors = []string{"Notified"}
func (ec *executionContext) _Notified(ctx context.Context, sel ast.SelectionSet, obj *Notified) graphql.Marshaler {
@ -27106,6 +27233,20 @@ func (ec *executionContext) marshalNNotificationFilter2githubᚗcomᚋjordanknot
return v
}
func (ec *executionContext) marshalNNotificationMarkAllAsReadResult2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐNotificationMarkAllAsReadResult(ctx context.Context, sel ast.SelectionSet, v NotificationMarkAllAsReadResult) graphql.Marshaler {
return ec._NotificationMarkAllAsReadResult(ctx, sel, &v)
}
func (ec *executionContext) marshalNNotificationMarkAllAsReadResult2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐNotificationMarkAllAsReadResult(ctx context.Context, sel ast.SelectionSet, v *NotificationMarkAllAsReadResult) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
return ec._NotificationMarkAllAsReadResult(ctx, sel, v)
}
func (ec *executionContext) unmarshalNNotificationToggleReadInput2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐNotificationToggleReadInput(ctx context.Context, v interface{}) (NotificationToggleReadInput, error) {
res, err := ec.unmarshalInputNotificationToggleReadInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err)

View File

@ -17,27 +17,38 @@ import (
"github.com/99designs/gqlgen/graphql/handler/lru"
"github.com/99designs/gqlgen/graphql/handler/transport"
"github.com/99designs/gqlgen/graphql/playground"
"github.com/go-redis/redis/v8"
"github.com/google/uuid"
"github.com/jordanknott/taskcafe/internal/config"
"github.com/jordanknott/taskcafe/internal/db"
"github.com/jordanknott/taskcafe/internal/jobs"
"github.com/jordanknott/taskcafe/internal/logger"
"github.com/jordanknott/taskcafe/internal/utils"
log "github.com/sirupsen/logrus"
"github.com/vektah/gqlparser/v2/gqlerror"
)
type NotificationObservers struct {
Mu sync.Mutex
Subscribers map[string]map[string]chan *Notified
}
// NewHandler returns a new graphql endpoint handler.
func NewHandler(repo db.Repository, appConfig config.AppConfig) http.Handler {
c := Config{
Resolvers: &Resolver{
Repository: repo,
AppConfig: appConfig,
Notifications: NotificationObservers{
Mu: sync.Mutex{},
Subscribers: make(map[string]map[string]chan *Notified),
},
func NewHandler(repo db.Repository, appConfig config.AppConfig, jobQueue jobs.JobQueue, redisClient *redis.Client) http.Handler {
resolver := &Resolver{
Repository: repo,
Redis: redisClient,
AppConfig: appConfig,
Job: jobQueue,
Notifications: &NotificationObservers{
Mu: sync.Mutex{},
Subscribers: make(map[string]map[string]chan *Notified),
},
}
resolver.SubscribeRedis()
c := Config{
Resolvers: resolver,
}
c.Directives.HasRole = func(ctx context.Context, obj interface{}, next graphql.Resolver, roles []RoleLevel, level ActionLevel, typeArg ObjectType) (interface{}, error) {
userID, ok := GetUser(ctx)
if !ok {

View File

@ -402,6 +402,10 @@ type NotificationData struct {
Value string `json:"value"`
}
type NotificationMarkAllAsReadResult struct {
Success bool `json:"success"`
}
type NotificationToggleReadInput struct {
NotifiedID uuid.UUID `json:"notifiedID"`
}
@ -749,6 +753,7 @@ const (
ActionTypeDueDateAdded ActionType = "DUE_DATE_ADDED"
ActionTypeDueDateRemoved ActionType = "DUE_DATE_REMOVED"
ActionTypeDueDateChanged ActionType = "DUE_DATE_CHANGED"
ActionTypeDueDateReminder ActionType = "DUE_DATE_REMINDER"
ActionTypeTaskAssigned ActionType = "TASK_ASSIGNED"
ActionTypeTaskMoved ActionType = "TASK_MOVED"
ActionTypeTaskArchived ActionType = "TASK_ARCHIVED"
@ -766,6 +771,7 @@ var AllActionType = []ActionType{
ActionTypeDueDateAdded,
ActionTypeDueDateRemoved,
ActionTypeDueDateChanged,
ActionTypeDueDateReminder,
ActionTypeTaskAssigned,
ActionTypeTaskMoved,
ActionTypeTaskArchived,
@ -776,7 +782,7 @@ var AllActionType = []ActionType{
func (e ActionType) IsValid() bool {
switch e {
case ActionTypeTeamAdded, ActionTypeTeamRemoved, ActionTypeProjectAdded, ActionTypeProjectRemoved, ActionTypeProjectArchived, ActionTypeDueDateAdded, ActionTypeDueDateRemoved, ActionTypeDueDateChanged, ActionTypeTaskAssigned, ActionTypeTaskMoved, ActionTypeTaskArchived, ActionTypeTaskAttachmentUploaded, ActionTypeCommentMentioned, ActionTypeCommentOther:
case ActionTypeTeamAdded, ActionTypeTeamRemoved, ActionTypeProjectAdded, ActionTypeProjectRemoved, ActionTypeProjectArchived, ActionTypeDueDateAdded, ActionTypeDueDateRemoved, ActionTypeDueDateChanged, ActionTypeDueDateReminder, ActionTypeTaskAssigned, ActionTypeTaskMoved, ActionTypeTaskArchived, ActionTypeTaskAttachmentUploaded, ActionTypeCommentMentioned, ActionTypeCommentOther:
return true
}
return false

View File

@ -70,6 +70,19 @@ func (r *mutationResolver) NotificationToggleRead(ctx context.Context, input Not
}, nil
}
func (r *mutationResolver) NotificationMarkAllRead(ctx context.Context) (*NotificationMarkAllAsReadResult, error) {
userID, ok := GetUserID(ctx)
if !ok {
return &NotificationMarkAllAsReadResult{}, errors.New("invalid user ID")
}
now := time.Now().UTC()
err := r.Repository.MarkAllNotificationsRead(ctx, db.MarkAllNotificationsReadParams{UserID: userID, ReadAt: sql.NullTime{Valid: true, Time: now}})
if err != nil {
return &NotificationMarkAllAsReadResult{}, err
}
return &NotificationMarkAllAsReadResult{Success: false}, nil
}
func (r *notificationResolver) ID(ctx context.Context, obj *db.Notification) (uuid.UUID, error) {
return obj.NotificationID, nil
}

View File

@ -4,20 +4,67 @@
package graph
import (
"sync"
"context"
"encoding/json"
"github.com/go-redis/redis/v8"
"github.com/google/uuid"
"github.com/jordanknott/taskcafe/internal/config"
"github.com/jordanknott/taskcafe/internal/db"
"github.com/jordanknott/taskcafe/internal/jobs"
"github.com/jordanknott/taskcafe/internal/utils"
log "github.com/sirupsen/logrus"
)
type NotificationObservers struct {
Subscribers map[string]map[string]chan *Notified
Mu sync.Mutex
}
// Resolver handles resolving GraphQL queries & mutations
type Resolver struct {
Repository db.Repository
AppConfig config.AppConfig
Notifications NotificationObservers
Notifications *NotificationObservers
Job jobs.JobQueue
Redis *redis.Client
}
func (r Resolver) SubscribeRedis() {
ctx := context.Background()
go func() {
subscriber := r.Redis.Subscribe(ctx, "notification-created")
log.Info("Stream starting...")
for {
msg, err := subscriber.ReceiveMessage(ctx)
if err != nil {
log.WithError(err).Error("while receiving message")
panic(err)
}
var message utils.NotificationCreatedMessage
if err := json.Unmarshal([]byte(msg.Payload), &message); err != nil {
log.WithError(err).Error("while unmarshalling notifiction created message")
panic(err)
}
log.WithField("notID", message.NotifiedID).Info("received notification message")
notified, err := r.Repository.GetNotifiedByIDNoExtra(ctx, uuid.MustParse(message.NotifiedID))
if err != nil {
log.WithError(err).Error("while getting notified by id")
panic(err)
}
notification, err := r.Repository.GetNotificationByID(ctx, uuid.MustParse(message.NotificationID))
if err != nil {
log.WithError(err).Error("while getting notified by id")
panic(err)
}
for _, observers := range r.Notifications.Subscribers {
for _, ochan := range observers {
ochan <- &Notified{
ID: notified.NotifiedID,
Read: notified.Read,
ReadAt: &notified.ReadAt.Time,
Notification: &notification,
}
}
}
}
}()
}

View File

@ -10,6 +10,10 @@ extend type Query {
extend type Mutation {
notificationToggleRead(input: NotificationToggleReadInput!): Notified!
notificationMarkAllRead: NotificationMarkAllAsReadResult!
}
type NotificationMarkAllAsReadResult {
success: Boolean!
}
type HasUnreadNotificationsResult {
@ -45,6 +49,7 @@ enum ActionType {
DUE_DATE_ADDED
DUE_DATE_REMOVED
DUE_DATE_CHANGED
DUE_DATE_REMINDER
TASK_ASSIGNED
TASK_MOVED
TASK_ARCHIVED

View File

@ -10,6 +10,10 @@ extend type Query {
extend type Mutation {
notificationToggleRead(input: NotificationToggleReadInput!): Notified!
notificationMarkAllRead: NotificationMarkAllAsReadResult!
}
type NotificationMarkAllAsReadResult {
success: Boolean!
}
type HasUnreadNotificationsResult {
@ -45,6 +49,7 @@ enum ActionType {
DUE_DATE_ADDED
DUE_DATE_REMOVED
DUE_DATE_CHANGED
DUE_DATE_REMINDER
TASK_ASSIGNED
TASK_MOVED
TASK_ARCHIVED

View File

@ -11,7 +11,10 @@ import (
"strconv"
"time"
mTasks "github.com/RichardKnop/machinery/v1/tasks"
"github.com/go-redis/redis/v8"
"github.com/google/uuid"
"github.com/jinzhu/now"
"github.com/jordanknott/taskcafe/internal/db"
"github.com/jordanknott/taskcafe/internal/logger"
log "github.com/sirupsen/logrus"
@ -539,6 +542,42 @@ func (r *mutationResolver) UpdateTaskDueDate(ctx context.Context, input UpdateTa
DueDate: dueDate,
HasTime: input.HasTime,
})
reminders, err := r.Repository.GetDueDateRemindersForTaskID(ctx, input.TaskID)
if err != nil {
log.WithError(err).Error("error while getting due date reminders for task ID")
return &db.Task{}, err
}
if input.DueDate != nil {
for _, rem := range reminders {
remindAt := now.With(*input.DueDate).BeginningOfDay()
if input.HasTime {
remindAt = *input.DueDate
}
switch rem.Duration {
case "MINUTE":
remindAt = remindAt.Add(time.Duration(-rem.Period) * time.Minute)
break
case "HOUR":
remindAt = remindAt.Add(time.Duration(-rem.Period) * time.Hour)
break
case "DAY":
remindAt = remindAt.AddDate(0, 0, int(-rem.Period))
break
case "WEEK":
remindAt = remindAt.AddDate(0, 0, 7*int(-rem.Period))
break
}
_, err := r.Repository.UpdateDueDateReminderRemindAt(ctx, db.UpdateDueDateReminderRemindAtParams{
DueDateReminderID: rem.DueDateReminderID,
RemindAt: remindAt,
})
if err != nil {
log.WithError(err).Error("error while updating due date reminder remind at")
return &db.Task{}, err
}
}
}
createdAt := time.Now().UTC()
d, _ := json.Marshal(data)
if !isSame {
@ -686,12 +725,55 @@ func (r *mutationResolver) UnassignTask(ctx context.Context, input *UnassignTask
func (r *mutationResolver) CreateTaskDueDateNotifications(ctx context.Context, input []CreateTaskDueDateNotification) (*CreateTaskDueDateNotificationsResult, error) {
reminders := []DueDateNotification{}
if len(input) == 0 {
return &CreateTaskDueDateNotificationsResult{}, nil
}
task, err := r.Repository.GetTaskByID(ctx, input[0].TaskID)
if err != nil {
log.WithError(err).Error("error while getting task by id")
return &CreateTaskDueDateNotificationsResult{}, nil
}
for _, in := range input {
remindAt := now.With(task.DueDate.Time).BeginningOfDay()
if task.HasTime {
remindAt = task.DueDate.Time
}
switch in.Duration {
case "MINUTE":
remindAt = remindAt.Add(time.Duration(-in.Period) * time.Minute)
break
case "HOUR":
remindAt = remindAt.Add(time.Duration(-in.Period) * time.Hour)
break
case "DAY":
remindAt = remindAt.AddDate(0, 0, int(-in.Period))
break
case "WEEK":
remindAt = remindAt.AddDate(0, 0, 7*int(-in.Period))
break
}
log.Info("task not found, sending task")
n, err := r.Repository.CreateDueDateReminder(ctx, db.CreateDueDateReminderParams{
TaskID: in.TaskID,
Period: int32(in.Period),
Duration: in.Duration.String(),
RemindAt: remindAt,
})
signature := &mTasks.Signature{
UUID: "due_date_reminder_" + n.DueDateReminderID.String(),
Name: "dueDateNotification",
ETA: &remindAt,
Args: []mTasks.Arg{{
Type: "string",
Value: n.DueDateReminderID.String(),
}, {
Type: "string",
Value: in.TaskID.String(),
}},
}
r.Job.Server.SendTask(signature)
if err != nil {
return &CreateTaskDueDateNotificationsResult{}, err
}
@ -713,15 +795,71 @@ func (r *mutationResolver) CreateTaskDueDateNotifications(ctx context.Context, i
func (r *mutationResolver) UpdateTaskDueDateNotifications(ctx context.Context, input []UpdateTaskDueDateNotification) (*UpdateTaskDueDateNotificationsResult, error) {
reminders := []DueDateNotification{}
if len(input) == 0 {
return &UpdateTaskDueDateNotificationsResult{}, nil
}
for _, in := range input {
task, err := r.Repository.GetTaskForDueDateReminder(ctx, in.ID)
if err != nil {
log.WithError(err).Error("error while getting task by id")
return &UpdateTaskDueDateNotificationsResult{}, nil
}
current, err := r.Repository.GetDueDateReminderByID(ctx, in.ID)
if err != nil {
log.WithError(err).Error("error while getting task by id")
return &UpdateTaskDueDateNotificationsResult{}, nil
}
remindAt := now.With(task.DueDate.Time).BeginningOfDay()
if task.HasTime {
remindAt = task.DueDate.Time
}
switch in.Duration {
case "MINUTE":
remindAt = remindAt.Add(time.Duration(-in.Period) * time.Minute)
break
case "HOUR":
remindAt = remindAt.Add(time.Duration(-in.Period) * time.Hour)
break
case "DAY":
remindAt = remindAt.AddDate(0, 0, int(-in.Period))
break
case "WEEK":
remindAt = remindAt.AddDate(0, 0, 7*int(-in.Period))
break
}
n, err := r.Repository.UpdateDueDateReminder(ctx, db.UpdateDueDateReminderParams{
DueDateReminderID: in.ID,
Period: int32(in.Period),
Duration: in.Duration.String(),
RemindAt: remindAt,
})
if err != nil {
return &UpdateTaskDueDateNotificationsResult{}, err
}
etaNano := strconv.FormatInt(current.RemindAt.UnixNano(), 10)
result, err := r.Redis.ZRangeByScore(ctx, "delayed_tasks", &redis.ZRangeBy{Max: etaNano, Min: etaNano}).Result()
if err != nil {
log.WithError(err).Error("error while getting due date reminder")
}
log.WithField("result", result).Info("result raw")
if len(result) != 0 {
r.Redis.ZRem(ctx, "delayed_tasks", result)
}
signature := &mTasks.Signature{
UUID: "due_date_reminder_" + n.DueDateReminderID.String(),
Name: "dueDateNotification",
ETA: &remindAt,
Args: []mTasks.Arg{{
Type: "string",
Value: n.DueDateReminderID.String(),
}, {
Type: "string",
Value: task.TaskID.String(),
}},
}
r.Job.Server.SendTask(signature)
duration := DueDateNotificationDuration(n.Duration)
if !duration.IsValid() {
log.WithField("duration", n.Duration).Error("invalid duration found")
@ -741,11 +879,21 @@ func (r *mutationResolver) UpdateTaskDueDateNotifications(ctx context.Context, i
func (r *mutationResolver) DeleteTaskDueDateNotifications(ctx context.Context, input []DeleteTaskDueDateNotification) (*DeleteTaskDueDateNotificationsResult, error) {
ids := []uuid.UUID{}
for _, n := range input {
err := r.Repository.DeleteDueDateReminder(ctx, n.ID)
reminder, err := r.Repository.GetDueDateReminderByID(ctx, n.ID)
err = r.Repository.DeleteDueDateReminder(ctx, n.ID)
if err != nil {
log.WithError(err).Error("error while deleting task due date notification")
return &DeleteTaskDueDateNotificationsResult{}, err
}
etaNano := strconv.FormatInt(reminder.RemindAt.UnixNano(), 10)
result, err := r.Redis.ZRangeByScore(ctx, "delayed_tasks", &redis.ZRangeBy{Max: etaNano, Min: etaNano}).Result()
if err != nil {
log.WithError(err).Error("error while getting due date reminder")
}
log.WithField("result", result).Info("result raw")
if len(result) != 0 {
r.Redis.ZRem(ctx, "delayed_tasks", result)
}
ids = append(ids, n.ID)
}
return &DeleteTaskDueDateNotificationsResult{Notifications: ids}, nil