Skip to content

Commit

Permalink
Merge pull request #175 from MurakawaTakuya/fix/172-fix-delete-tasks-…
Browse files Browse the repository at this point in the history
…on-post-create

目標完了時にCloud Tasksの通知が削除されない問題を修正
  • Loading branch information
MurakawaTakuya authored Jan 28, 2025
2 parents fbead8d + 8b49326 commit 7aee594
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 51 deletions.
2 changes: 1 addition & 1 deletion functions/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export const firestore = onRequest({ region: region }, async (req, res) => {
export {
createTasksOnGoalCreate,
deleteTasksOnGoalDelete,
deleteTasksOnPostCreate,
updateTasksOnPostUpdate,
} from "./tasks";

// テスト用API
Expand Down
190 changes: 140 additions & 50 deletions functions/src/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,60 @@ const tasksClient = new CloudTasksClient();
const projectId = process.env.GCP_PROJECT_ID;
const region = "asia-northeast1";
const queue = "deadline-notification-queue";
const marginTime = 10;

// queuePath, postData, etaを受け取り、タスクを作成
const createTask = async (
queuePath: string,
postData: { goalId: string; marginTime: number },
eta: Date
) => {
const accessToken = process.env.NOTIFICATION_KEY;
const goalId = postData.goalId;

const now = new Date();
// タスクの名前をgoalIdと作成日時を結合したものに設定
const name = `${queuePath}/tasks/${goalId}-${now.getTime()}`;
logger.info(`Creating task: ${name}`);
await tasksClient.createTask({
parent: queuePath,
task: {
name,
httpRequest: {
httpMethod: "POST",
url: "https://firestore-okdtj725ta-an.a.run.app/notification",
headers: {
"Content-Type": "application/json",
token: `${accessToken}`,
},
body: Buffer.from(JSON.stringify(postData)).toString("base64"),
},
scheduleTime: { seconds: Math.floor(eta.getTime() / 1000) },
},
});
logger.info(`Task created for ${goalId} with ${name}`);
};

// queuePath, goalIdで始まるタスクを削除
const deleteTask = async (queuePath: string, goalId: string) => {
const taskPrefix = `${queuePath}/tasks/${goalId}`;
logger.info(`Deleting tasks for ${goalId} with prefix ${taskPrefix}`);

const [tasks] = await tasksClient.listTasks({ parent: queuePath });
const tasksToDelete = tasks.filter(
(task) => task.name && task.name.startsWith(taskPrefix)
);

for (const task of tasksToDelete) {
if (task.name) {
logger.info("Deleting task:", task.name);
await tasksClient.deleteTask({ name: task.name });
logger.info("Task deleted:", task.name);
}
}

logger.info("All tasks with prefix deleted for goalId:", goalId);
};

export const createTasksOnGoalCreate = onDocumentCreated(
{ region: region, document: "goal/{goalId}" },
Expand All @@ -32,45 +86,31 @@ export const createTasksOnGoalCreate = onDocumentCreated(

try {
const goalData = event.data.data();
const marginTime = 10;
const postData = {
goalId: event.params.goalId,
marginTime,
};
// 期限のmarginTime分前にタスクを設定
const deadline = new Date(
goalData.deadline.toDate().getTime() - marginTime * 60 * 1000
);
const goalId = event.params.goalId;
const postData = {
goalId,
marginTime,
};
const queuePath = tasksClient.queuePath(projectId, region, queue);
const accessToken = process.env.NOTIFICATION_KEY;

await tasksClient.createTask({
parent: queuePath,
task: {
name: `${queuePath}/tasks/${goalId}`, // タスクの名前をgoalIdに設定
httpRequest: {
httpMethod: "POST",
url: "https://firestore-okdtj725ta-an.a.run.app/notification",
headers: {
"Content-Type": "application/json",
token: `${accessToken}`,
},
body: Buffer.from(JSON.stringify(postData)).toString("base64"),
},
scheduleTime: { seconds: Math.floor(deadline.getTime() / 1000) },
},
});
logger.info("Task created for goalId:", goalId);
createTask(
tasksClient.queuePath(projectId, region, queue),
postData,
deadline
);
} catch (error) {
logger.error("Error scheduling task:", error);
}
}
);

// 目標を削除した時にtasksから通知予定を削除する
export const deleteTasksOnGoalDelete = onDocumentDeleted(
{ region: region, document: "goal/{goalId}" },
// 目標を完了した時にtasksから通知予定を削除する
export const updateTasksOnPostUpdate = onDocumentUpdated(
{
region: region,
document: "goal/{goalId}",
},
async (event) => {
if (process.env.NODE_ENV !== "production") {
return;
Expand All @@ -86,24 +126,80 @@ export const deleteTasksOnGoalDelete = onDocumentDeleted(
return;
}

try {
const goalId = event.params.goalId;
const queuePath = tasksClient.queuePath(projectId, region, queue);
const taskName = `${queuePath}/tasks/${goalId}`; // goalIdが名前になっているタスクを削除
await tasksClient.deleteTask({ name: taskName });
logger.info("Task deleted for goalId:", goalId);
} catch (error) {
logger.error("Error deleting task:", error);
if (
event.data.before.data().post === null &&
event.data.after.data().post !== null
) {
// 目標を完了(完了投稿を作成)した場合
// null -> post に変更された場合はタスクを削除
try {
const goalId = event.params.goalId;
const queuePath = tasksClient.queuePath(projectId, region, queue);
await deleteTask(queuePath, goalId);
} catch (error) {
logger.error("Error deleting task:", error);
}
} else if (
event.data.before.data().post !== null &&
event.data.after.data().post === null
) {
// 完了投稿を削除した場合
// post -> null に変更された場合はタスクを作成(deadlineが今よりも後の場合のみ)
if (
event.data.after.data().deadline.toDate().getTime() <=
new Date().getTime()
) {
return;
}

// taskを作成
try {
const postData = {
goalId: event.params.goalId,
marginTime,
};
createTask(
tasksClient.queuePath(projectId, region, queue),
postData,
event.data.after.data().deadline.toDate()
);
} catch (error) {
logger.error("Error scheduling task:", error);
}
} else if (
event.data.before.data().post === null &&
event.data.after.data().post === null
) {
// deadlineが変更された場合はタスクを削除して再作成
if (event.data.before.data() === event.data.after.data()) {
return;
}
try {
// taskを削除
const goalId = event.params.goalId;
const queuePath = tasksClient.queuePath(projectId, region, queue);
await deleteTask(queuePath, goalId);

const postData = {
goalId: event.params.goalId,
marginTime,
};
// taskを作成
createTask(
queuePath,
postData,
event.data.after.data().deadline.toDate()
);
} catch (error) {
logger.error("Error updating task:", error);
}
}
}
);

// 目標を完了した時にtasksから通知予定を削除する
export const deleteTasksOnPostCreate = onDocumentUpdated(
{
region: region,
document: "goal/{goalId}",
},
// 目標を削除した時にtasksから通知予定を削除する
export const deleteTasksOnGoalDelete = onDocumentDeleted(
{ region: region, document: "goal/{goalId}" },
async (event) => {
if (process.env.NODE_ENV !== "production") {
return;
Expand All @@ -119,16 +215,10 @@ export const deleteTasksOnPostCreate = onDocumentUpdated(
return;
}

if (event.data.after.data().post !== null) {
return;
}

try {
const goalId = event.params.goalId;
const queuePath = tasksClient.queuePath(projectId, region, queue);
const taskName = `${queuePath}/tasks/${goalId}`; // goalIdが名前になっているタスクを削除
await tasksClient.deleteTask({ name: taskName });
logger.info("Task deleted for goalId:", goalId);
await deleteTask(queuePath, goalId);
} catch (error) {
logger.error("Error deleting task:", error);
}
Expand Down

0 comments on commit 7aee594

Please sign in to comment.