diff --git a/core/grant/service.go b/core/grant/service.go index 20e8327b3..9ea31a934 100644 --- a/core/grant/service.go +++ b/core/grant/service.go @@ -2,8 +2,10 @@ package grant import ( "context" + "encoding/json" "fmt" "sort" + "strings" "time" "github.com/go-playground/validator/v10" @@ -540,6 +542,7 @@ func (s *Service) DormancyCheck(ctx context.Context, criteria domain.DormancyChe s.logger.Info("checking grants dormancy...") var dormantGrants []*domain.Grant var dormantGrantsIDs []string + var dormantGrantsByOwner = map[string][]*domain.Grant{} for _, g := range grantsPointer { if len(g.Activities) == 0 { g.ExpirationDateReason = fmt.Sprintf("%s: %s", domain.GrantExpirationReasonDormant, criteria.RetainDuration) @@ -548,6 +551,8 @@ func (s *Service) DormancyCheck(ctx context.Context, criteria domain.DormancyChe dormantGrants = append(dormantGrants, g) dormantGrantsIDs = append(dormantGrantsIDs, g.ID) + + dormantGrantsByOwner[g.Owner] = append(dormantGrantsByOwner[g.Owner], g) } } s.logger.Info(fmt.Sprintf("found %d dormant grants for provider %q", len(dormantGrants), provider.URN), "grant_ids", dormantGrantsIDs) @@ -560,6 +565,48 @@ func (s *Service) DormancyCheck(ctx context.Context, criteria domain.DormancyChe if err := s.repo.BulkUpsert(ctx, dormantGrants); err != nil { return fmt.Errorf("updating grants expiration date: %w", err) } + + go func() { // send notifications + var notifications []domain.Notification + prepare_notifications: + for owner, grants := range dormantGrantsByOwner { + var grantsMap []map[string]interface{} + var grantIDs []string + + for _, g := range grants { + grantMap, err := structToMap(g) + if err != nil { + s.logger.Error("failed to convert grant to map", "error", err) + continue prepare_notifications + } + grantsMap = append(grantsMap, grantMap) + } + + notifications = append(notifications, domain.Notification{ + User: owner, + Labels: map[string]string{ + "owner": owner, + "grant_ids": strings.Join(grantIDs, ", "), + }, + Message: domain.NotificationMessage{ + Type: domain.NotificationTypeUnusedGrant, + Variables: map[string]interface{}{ + "dormant_grants": grantsMap, + "period": criteria.Period.String(), + "retain_duration": criteria.RetainDuration.String(), + "start_date_formatted": startDate.Format("Jan 02, 2006 15:04:05 UTC"), + }, + }, + }) + } + + if errs := s.notifier.Notify(notifications); errs != nil { + for _, err1 := range errs { + s.logger.Error("failed to send notifications", "error", err1.Error()) + } + } + }() + return nil } @@ -624,3 +671,20 @@ func getGrantIDs(grants []domain.Grant) []string { } return ids } + +func structToMap(item interface{}) (map[string]interface{}, error) { + result := map[string]interface{}{} + + if item != nil { + jsonString, err := json.Marshal(item) + if err != nil { + return nil, err + } + + if err := json.Unmarshal(jsonString, &result); err != nil { + return nil, err + } + } + + return result, nil +} diff --git a/domain/notifier.go b/domain/notifier.go index 8ffa529d5..1aec9efbc 100644 --- a/domain/notifier.go +++ b/domain/notifier.go @@ -8,6 +8,7 @@ type NotificationMessages struct { ApproverNotification string `mapstructure:"approver_notification"` OthersAppealApproved string `mapstructure:"others_appeal_approved"` GrantOwnerChanged string `mapstructure:"grant_owner_changed"` + UnusedGrant string `mapstructure:"unused_grant"` } const ( @@ -18,6 +19,7 @@ const ( NotificationTypeAccessRevoked = "AccessRevoked" NotificationTypeApproverNotification = "ApproverNotification" NotificationTypeGrantOwnerChanged = "GrantOwnerChanged" + NotificationTypeUnusedGrant = "UnusedGrant" ) type NotificationMessage struct { diff --git a/jobs/grant_dormancy_check.go b/jobs/grant_dormancy_check.go index e87b77777..f065048ba 100644 --- a/jobs/grant_dormancy_check.go +++ b/jobs/grant_dormancy_check.go @@ -35,6 +35,9 @@ func (h *handler) GrantDormancyCheck(ctx context.Context, c Config) error { } for _, p := range providers { + if p.URN != "pilotdata-integration-bigquery" { + continue + } h.logger.Info(fmt.Sprintf("checking dormancy for grants under provider: %q", p.URN)) if err := h.grantService.DormancyCheck(ctx, domain.DormancyCheckCriteria{ ProviderID: p.ID, diff --git a/plugins/notifiers/slack/templates/UnusedGrant.json b/plugins/notifiers/slack/templates/UnusedGrant.json new file mode 100644 index 000000000..cf6fa7fb8 --- /dev/null +++ b/plugins/notifiers/slack/templates/UnusedGrant.json @@ -0,0 +1,9 @@ +[ + { + "type":"section", + "text":{ + "type":"mrkdwn", + "text":"We have advanced the expiration date for the following grants due to inactivity since `{{.start_date}}`:{{ range .dormant_grants }}\n>*ID*: `{{.id}}`\n>*Account ID*: `{{.account_id}}`\n>*Resource*: `{{.resource.urn}}` ({{.resource.provider_type}} {{.resource.type}})\n>*Role*: `{{.role}}`\n>*Expiration Date* (new): `{{.expiration_date}}`\n{{end}}" + } + } +] \ No newline at end of file