Update ReactiveCache call sites to use async/await for Meteor 3.0

Part 3 of ReactiveCache async migration:
- Add await before all ReactiveCache.getX() calls
- Make functions containing ReactiveCache calls async
- Convert forEach/map/filter loops with async callbacks to for...of
- Update model helpers, Meteor methods, JsonRoutes handlers
- Update collection hooks (.before/.after insert/update/remove)
- Update .allow() callbacks to async

Files updated across models/ and server/ directories:
- Model files: cards, boards, lists, swimlanes, activities, users,
  checklists, checklistItems, customFields, attachments, integrations,
  cardComments, settings files, creators, exporters, and more
- Server files: publications, methods, notifications, routes, migrations
This commit is contained in:
Harry Adel 2026-02-01 00:54:38 +02:00
parent 2f6e34c5f5
commit 71eb01e233
81 changed files with 2218 additions and 2148 deletions

View file

@ -180,30 +180,30 @@ Lists.attachSchema(
);
Lists.allow({
insert(userId, doc) {
async insert(userId, doc) {
// ReadOnly and CommentOnly users cannot create lists
return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId));
return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId));
},
update(userId, doc) {
async update(userId, doc) {
// ReadOnly and CommentOnly users cannot edit lists
return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId));
return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId));
},
remove(userId, doc) {
async remove(userId, doc) {
// ReadOnly and CommentOnly users cannot delete lists
return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId));
return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId));
},
fetch: ['boardId'],
});
Lists.helpers({
copy(boardId, swimlaneId) {
async copy(boardId, swimlaneId) {
const oldId = this._id;
const oldSwimlaneId = this.swimlaneId || null;
this.boardId = boardId;
this.swimlaneId = swimlaneId;
let _id = null;
const existingListWithSameName = ReactiveCache.getList({
const existingListWithSameName = await ReactiveCache.getList({
boardId,
title: this.title,
archived: false,
@ -213,21 +213,22 @@ Lists.helpers({
} else {
delete this._id;
this.swimlaneId = swimlaneId; // Set the target swimlane for the copied list
_id = Lists.insert(this);
_id = await Lists.insertAsync(this);
}
// Copy all cards in list
ReactiveCache.getCards({
const cards = await ReactiveCache.getCards({
swimlaneId: oldSwimlaneId,
listId: oldId,
archived: false,
}).forEach(card => {
card.copy(boardId, swimlaneId, _id);
});
for (const card of cards) {
await card.copy(boardId, swimlaneId, _id);
}
},
async move(boardId, swimlaneId) {
const boardList = ReactiveCache.getList({
const boardList = await ReactiveCache.getList({
boardId,
title: this.title,
archived: false,
@ -235,13 +236,13 @@ Lists.helpers({
let listId;
if (boardList) {
listId = boardList._id;
for (const card of this.cards()) {
for (const card of await this.cards()) {
await card.move(boardId, this._id, boardList._id);
}
} else {
console.log('list.title:', this.title);
console.log('boardList:', boardList);
listId = Lists.insert({
listId = await Lists.insertAsync({
title: this.title,
boardId,
type: this.type,
@ -251,42 +252,42 @@ Lists.helpers({
});
}
for (const card of this.cards(swimlaneId)) {
for (const card of await this.cards(swimlaneId)) {
await card.move(boardId, swimlaneId, listId);
}
},
cards(swimlaneId) {
async cards(swimlaneId) {
const selector = {
listId: this._id,
archived: false,
};
if (swimlaneId) selector.swimlaneId = swimlaneId;
const ret = ReactiveCache.getCards(Filter.mongoSelector(selector), { sort: ['sort'] });
const ret = await ReactiveCache.getCards(Filter.mongoSelector(selector), { sort: ['sort'] });
return ret;
},
cardsUnfiltered(swimlaneId) {
async cardsUnfiltered(swimlaneId) {
const selector = {
listId: this._id,
archived: false,
};
if (swimlaneId) selector.swimlaneId = swimlaneId;
const ret = ReactiveCache.getCards(selector, { sort: ['sort'] });
const ret = await ReactiveCache.getCards(selector, { sort: ['sort'] });
return ret;
},
allCards() {
const ret = ReactiveCache.getCards({ listId: this._id });
async allCards() {
const ret = await ReactiveCache.getCards({ listId: this._id });
return ret;
},
board() {
return ReactiveCache.getBoard(this.boardId);
async board() {
return await ReactiveCache.getBoard(this.boardId);
},
getWipLimit(option) {
const list = ReactiveCache.getList(this._id);
async getWipLimit(option) {
const list = await ReactiveCache.getList(this._id);
if (!list.wipLimit) {
// Necessary check to avoid exceptions for the case where the doc doesn't have the wipLimit field yet set
return 0;
@ -310,9 +311,9 @@ Lists.helpers({
return this.starred === true;
},
isCollapsed() {
async isCollapsed() {
if (Meteor.isClient) {
const user = ReactiveCache.getCurrentUser();
const user = await ReactiveCache.getCurrentUser();
// Logged-in users: prefer profile/cookie-backed state
if (user && user.getCollapsedListFromStorage) {
const stored = user.getCollapsedListFromStorage(this.boardId, this._id);
@ -331,13 +332,13 @@ Lists.helpers({
return this.collapsed === true;
},
absoluteUrl() {
const card = ReactiveCache.getCard({ listId: this._id });
return card && card.absoluteUrl();
async absoluteUrl() {
const card = await ReactiveCache.getCard({ listId: this._id });
return card && (await card.absoluteUrl());
},
originRelativeUrl() {
const card = ReactiveCache.getCard({ listId: this._id });
return card && card.originRelativeUrl();
async originRelativeUrl() {
const card = await ReactiveCache.getCard({ listId: this._id });
return card && (await card.originRelativeUrl());
},
async remove() {
return await Lists.removeAsync({ _id: this._id });
@ -361,7 +362,7 @@ Lists.helpers({
async archive() {
if (this.isTemplateList()) {
for (const card of this.cards()) {
for (const card of await this.cards()) {
await card.archive();
}
}
@ -370,7 +371,7 @@ Lists.helpers({
async restore() {
if (this.isTemplateList()) {
for (const card of this.allCards()) {
for (const card of await this.allCards()) {
await card.restore();
}
}
@ -394,23 +395,25 @@ Lists.helpers({
},
});
Lists.userArchivedLists = userId => {
return ReactiveCache.getLists({
boardId: { $in: Boards.userBoardIds(userId, null) },
Lists.userArchivedLists = async userId => {
return await ReactiveCache.getLists({
boardId: { $in: await Boards.userBoardIds(userId, null) },
archived: true,
})
};
Lists.userArchivedListIds = () => {
return Lists.userArchivedLists().map(list => { return list._id; });
Lists.userArchivedListIds = async () => {
const lists = await Lists.userArchivedLists();
return lists.map(list => { return list._id; });
};
Lists.archivedLists = () => {
return ReactiveCache.getLists({ archived: true });
Lists.archivedLists = async () => {
return await ReactiveCache.getLists({ archived: true });
};
Lists.archivedListIds = () => {
return Lists.archivedLists().map(list => {
Lists.archivedListIds = async () => {
const lists = await Lists.archivedLists();
return lists.map(list => {
return list._id;
});
};
@ -424,12 +427,12 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
const list = ReactiveCache.getList(listId);
const list = await ReactiveCache.getList(listId);
if (!list) {
throw new Meteor.Error('list-not-found', 'List not found');
}
const board = ReactiveCache.getBoard(list.boardId);
const board = await ReactiveCache.getBoard(list.boardId);
if (!board || !board.hasAdmin(this.userId)) {
throw new Meteor.Error('not-authorized', 'You must be a board admin to modify WIP limits.');
}
@ -447,63 +450,62 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
const list = ReactiveCache.getList(listId);
const list = await ReactiveCache.getList(listId);
if (!list) {
throw new Meteor.Error('list-not-found', 'List not found');
}
const board = ReactiveCache.getBoard(list.boardId);
const board = await ReactiveCache.getBoard(list.boardId);
if (!board || !board.hasAdmin(this.userId)) {
throw new Meteor.Error('not-authorized', 'You must be a board admin to modify WIP limits.');
}
if (list.getWipLimit('value') === 0) {
if ((await list.getWipLimit('value')) === 0) {
await list.setWipLimit(1);
}
list.toggleWipLimit(!list.getWipLimit('enabled'));
await list.toggleWipLimit(!(await list.getWipLimit('enabled')));
},
enableSoftLimit(listId) {
async enableSoftLimit(listId) {
check(listId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
const list = ReactiveCache.getList(listId);
const list = await ReactiveCache.getList(listId);
if (!list) {
throw new Meteor.Error('list-not-found', 'List not found');
}
const board = ReactiveCache.getBoard(list.boardId);
const board = await ReactiveCache.getBoard(list.boardId);
if (!board || !board.hasAdmin(this.userId)) {
throw new Meteor.Error('not-authorized', 'You must be a board admin to modify WIP limits.');
}
list.toggleSoftLimit(!list.getWipLimit('soft'));
await list.toggleSoftLimit(!(await list.getWipLimit('soft')));
},
myLists() {
async myLists() {
// my lists
return _.uniq(
ReactiveCache.getLists(
{
boardId: { $in: Boards.userBoardIds(this.userId) },
archived: false,
},
{
fields: { title: 1 },
},
).map(list => list.title),
).sort();
const lists = await ReactiveCache.getLists(
{
boardId: { $in: Boards.userBoardIds(this.userId) },
archived: false,
},
{
fields: { title: 1 },
},
);
return _.uniq(lists.map(list => list.title)).sort();
},
updateListSort(listId, boardId, updateData) {
async updateListSort(listId, boardId, updateData) {
check(listId, String);
check(boardId, String);
check(updateData, Object);
const board = ReactiveCache.getBoard(boardId);
const board = await ReactiveCache.getBoard(boardId);
if (!board) {
throw new Meteor.Error('board-not-found', 'Board not found');
}
@ -516,7 +518,7 @@ Meteor.methods({
}
}
const list = ReactiveCache.getList(listId);
const list = await ReactiveCache.getList(listId);
if (!list) {
throw new Meteor.Error('list-not-found', 'List not found');
}
@ -529,13 +531,13 @@ Meteor.methods({
});
if (updateData.swimlaneId) {
const swimlane = ReactiveCache.getSwimlane(updateData.swimlaneId);
const swimlane = await ReactiveCache.getSwimlane(updateData.swimlaneId);
if (!swimlane || swimlane.boardId !== boardId) {
throw new Meteor.Error('invalid-swimlane', 'Invalid swimlane for this board');
}
}
Lists.update(
await Lists.updateAsync(
{ _id: listId, boardId },
{
$set: {
@ -583,14 +585,14 @@ Lists.after.insert((userId, doc) => {
}, 100);
});
Lists.before.remove((userId, doc) => {
const cards = ReactiveCache.getCards({ listId: doc._id });
Lists.before.remove(async (userId, doc) => {
const cards = await ReactiveCache.getCards({ listId: doc._id });
if (cards) {
cards.forEach(card => {
Cards.remove(card._id);
});
for (const card of cards) {
await Cards.removeAsync(card._id);
}
}
Activities.insert({
await Activities.insertAsync({
userId,
type: 'list',
activityType: 'removeList',
@ -722,12 +724,12 @@ if (Meteor.isServer) {
* @param {string} title the title of the List
* @return_type {_id: string}
*/
JsonRoutes.add('POST', '/api/boards/:boardId/lists', function(req, res) {
JsonRoutes.add('POST', '/api/boards/:boardId/lists', async function(req, res) {
try {
const paramBoardId = req.params.boardId;
Authentication.checkBoardWriteAccess(req.userId, paramBoardId);
const board = ReactiveCache.getBoard(paramBoardId);
const id = Lists.insert({
const board = await ReactiveCache.getBoard(paramBoardId);
const id = await Lists.insertAsync({
title: req.body.title,
boardId: paramBoardId,
sort: board.lists().length,
@ -763,7 +765,7 @@ if (Meteor.isServer) {
* @param {boolean} [collapsed] whether the list is collapsed
* @return_type {_id: string}
*/
JsonRoutes.add('PUT', '/api/boards/:boardId/lists/:listId', function(
JsonRoutes.add('PUT', '/api/boards/:boardId/lists/:listId', async function(
req,
res,
) {
@ -773,7 +775,7 @@ if (Meteor.isServer) {
let updated = false;
Authentication.checkBoardWriteAccess(req.userId, paramBoardId);
const list = ReactiveCache.getList({
const list = await ReactiveCache.getList({
_id: paramListId,
boardId: paramBoardId,
archived: false,