Raw
 1package storage
 2
 3import (
 4	"context"
 5	"database/sql"
 6	"fmt"
 7	"time"
 8)
 9
10func (s *Store) RevokeInvitationBatchAudited(ctx context.Context, batchID, actor string) (int, error) {
11	const operation = "revoke invitation batch"
12	var revoked int
13	err := s.audited(ctx, operation, actor, func(tx *sql.Tx, at time.Time) error {
14		var members int
15		if err := tx.QueryRowContext(ctx, `SELECT count(*) FROM invitations WHERE batch_id = ?`, batchID).Scan(&members); err != nil {
16			return classify(operation, err)
17		}
18		if members == 0 {
19			return fmt.Errorf("%s: %w", operation, ErrNotFound)
20		}
21		ids, err := queryIDs(ctx, tx, operation, `
22SELECT id FROM invitations
23WHERE batch_id = ? AND used_at IS NULL AND revoked_at IS NULL AND expires_at > ?
24ORDER BY id`, batchID, timeToInt(at))
25		if err != nil {
26			return err
27		}
28		for _, id := range ids {
29			if _, err := execCount(ctx, tx, operation, `UPDATE invitations SET revoked_at = ? WHERE id = ? AND used_at IS NULL AND revoked_at IS NULL`, timeToInt(at), id); err != nil {
30				return err
31			}
32			if err := writeAudit(ctx, tx, operation, actor, "invitation.revoke", id, at, map[string]any{"batch": batchID}); err != nil {
33				return err
34			}
35		}
36		revoked = len(ids)
37		return nil
38	})
39	if err != nil {
40		return 0, err
41	}
42	return revoked, nil
43}