-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcleanup.ts
More file actions
164 lines (138 loc) · 5.54 KB
/
Copy pathcleanup.ts
File metadata and controls
164 lines (138 loc) · 5.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import { onDocumentDeleted, onDocumentUpdated } from "firebase-functions/v2/firestore";
import { onSchedule } from "firebase-functions/v2/scheduler";
import * as admin from "firebase-admin";
import * as logger from "firebase-functions/logger";
const LOCATION = "asia-northeast3";
const DELETE_BATCH_SIZE = 200;
const QUERY_BATCH_SIZE = 100;
export const removeTodoNotificationDocuments = onDocumentDeleted({
maxInstances: 1,
document: "users/{userId}/todoLists/{todoId}",
region: LOCATION
},
async (event) => {
const userId = event.params.userId;
const todoId = event.params.todoId;
try {
await deleteByTodoId(userId, "notificationReceipts", todoId);
await deleteByTodoId(userId, "notifications", todoId);
} catch (error) {
logger.error("todo 삭제 후 notification 문서 정리 실패", {
userId,
todoId,
error
});
}
}
);
export const removeCompletedTodoNotificationRecords = onDocumentUpdated({
maxInstances: 1,
document: "users/{userId}/todoLists/{todoId}",
region: LOCATION
},
async (event) => {
const beforeData = event.data?.before.data();
const afterData = event.data?.after.data();
const userId = event.params.userId;
const todoId = event.params.todoId;
if (!beforeData || !afterData) { return; }
if (beforeData.isCompleted === true || afterData.isCompleted !== true) { return; }
const dueDateValue = afterData.dueDate;
let dueDate: Date | null = null;
if (dueDateValue instanceof admin.firestore.Timestamp) {
dueDate = dueDateValue.toDate();
} else if (dueDateValue instanceof Date) {
dueDate = dueDateValue;
}
if (!dueDate || Date.now() <= dueDate.getTime()) { return; }
try {
await deleteByTodoId(userId, "notificationReceipts", todoId);
} catch (error) {
logger.error("완료된 todo의 notification record 정리 실패", {
userId,
todoId,
error
});
}
}
);
export const cleanupUnusedTodoNotificationRecords = onSchedule({
maxInstances: 1,
region: LOCATION,
schedule: "0 * * * *",
timeZone: "UTC"
},
async () => {
try {
let lastExpiredCompletedTodo:
FirebaseFirestore.QueryDocumentSnapshot<FirebaseFirestore.DocumentData> | undefined;
while (true) {
let query = admin.firestore()
.collectionGroup("todoLists")
.where("isCompleted", "==", true)
.where("dueDate", "<", admin.firestore.Timestamp.now())
.orderBy("dueDate")
.limit(QUERY_BATCH_SIZE);
if (lastExpiredCompletedTodo) {
query = query.startAfter(lastExpiredCompletedTodo);
}
const snapshot = await query.get();
if (snapshot.empty) { break; }
for (const todoDoc of snapshot.docs) {
const userId = todoDoc.ref.parent.parent?.id;
if (!userId) { continue; }
await deleteByTodoId(userId, "notificationReceipts", todoDoc.id);
}
if (snapshot.size < QUERY_BATCH_SIZE) { break; }
lastExpiredCompletedTodo = snapshot.docs[snapshot.docs.length - 1];
}
} catch (error) {
logger.error("지난 마감일의 완료된 todo notification record 정리 실패", { error });
}
try {
let lastTodoWithoutDueDate:
FirebaseFirestore.QueryDocumentSnapshot<FirebaseFirestore.DocumentData> | undefined;
while (true) {
let query = admin.firestore()
.collectionGroup("todoLists")
.where("dueDate", "==", null)
.orderBy(admin.firestore.FieldPath.documentId())
.limit(QUERY_BATCH_SIZE);
if (lastTodoWithoutDueDate) {
query = query.startAfter(lastTodoWithoutDueDate);
}
const snapshot = await query.get();
if (snapshot.empty) { break; }
for (const todoDoc of snapshot.docs) {
const userId = todoDoc.ref.parent.parent?.id;
if (!userId) { continue; }
await deleteByTodoId(userId, "notificationReceipts", todoDoc.id);
}
if (snapshot.size < QUERY_BATCH_SIZE) { break; }
lastTodoWithoutDueDate = snapshot.docs[snapshot.docs.length - 1];
}
} catch (error) {
logger.error("마감일이 없는 todo notification record 정리 실패", { error });
}
}
);
async function deleteByTodoId(
userId: string,
collectionName: "notificationReceipts" | "notifications",
todoId: string
): Promise<void> {
while (true) {
const snapshot = await admin.firestore()
.collection(`users/${userId}/${collectionName}`)
.where("todoId", "==", todoId)
.limit(DELETE_BATCH_SIZE)
.get();
if (snapshot.empty) { return; }
const batch = admin.firestore().batch();
snapshot.docs.forEach((document) => {
batch.delete(document.ref);
});
await batch.commit();
if (snapshot.size < DELETE_BATCH_SIZE) { return; }
}
}