mirror of
https://github.com/wekan/wekan.git
synced 2026-02-21 07:24:07 +01:00
187 lines
5.8 KiB
Markdown
187 lines
5.8 KiB
Markdown
# Meteor 3.0 Migration Guide
|
|
|
|
Reference document capturing patterns, constraints, and lessons learned during the async migration of WeKan from Meteor 2.16 toward Meteor 3.0 readiness.
|
|
|
|
---
|
|
|
|
## 1. Dual-Compatibility Strategy
|
|
|
|
WeKan runs on **Meteor 2.16 with Blaze 2.x**. The goal is dual compatibility: changes must work on 2.16 now and remain compatible with a future Meteor 3.0 upgrade.
|
|
|
|
**Key constraint:** Blaze 2.x does NOT support async template helpers. Client-side code must receive synchronous data.
|
|
|
|
---
|
|
|
|
## 2. ReactiveCache Facade Pattern
|
|
|
|
`ReactiveCache` dispatches to `ReactiveCacheServer` (async MongoDB) or `ReactiveCacheClient` (sync Minimongo).
|
|
|
|
**Rule:** Facade methods must NOT be `async`. They return a Promise on the server and data on the client. Server callers `await`; client code uses the return value directly.
|
|
|
|
```javascript
|
|
// CORRECT:
|
|
getBoard(boardId) {
|
|
if (Meteor.isServer) {
|
|
return ReactiveCacheServer.getBoard(boardId); // Returns Promise
|
|
} else {
|
|
return ReactiveCacheClient.getBoard(boardId); // Returns data
|
|
}
|
|
}
|
|
|
|
// WRONG:
|
|
async getBoard(boardId) { ... } // Wraps client return in Promise too!
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Model Helpers (Collection.helpers)
|
|
|
|
Model helpers defined via `Collection.helpers({})` are used by Blaze templates. They must NOT be `async`.
|
|
|
|
```javascript
|
|
// CORRECT:
|
|
Cards.helpers({
|
|
board() {
|
|
return ReactiveCache.getBoard(this.boardId); // Promise on server, data on client
|
|
},
|
|
});
|
|
|
|
// WRONG:
|
|
Cards.helpers({
|
|
async board() { // Blaze gets Promise instead of data
|
|
return await ReactiveCache.getBoard(this.boardId);
|
|
},
|
|
});
|
|
```
|
|
|
|
**Server-side callers** of these helpers must `await` the result:
|
|
```javascript
|
|
// In a Meteor method or hook (server-only):
|
|
const board = await card.board();
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Allow/Deny Callbacks Must Be Synchronous
|
|
|
|
Meteor 2.x evaluates allow/deny callbacks synchronously. An `async` callback returns a Promise:
|
|
- **allow** callback returning Promise (truthy) → always passes
|
|
- **deny** callback returning Promise (truthy) → always denies
|
|
|
|
**Rule:** Never use `async` in allow/deny. Replace `ReactiveCache` calls with direct sync Mongo calls.
|
|
|
|
```javascript
|
|
// CORRECT:
|
|
Cards.allow({
|
|
insert(userId, doc) {
|
|
return allowIsBoardMemberWithWriteAccess(userId, Boards.findOne(doc.boardId));
|
|
},
|
|
fetch: ['boardId'],
|
|
});
|
|
|
|
// WRONG:
|
|
Cards.allow({
|
|
async insert(userId, doc) {
|
|
return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId));
|
|
},
|
|
});
|
|
```
|
|
|
|
### Sync alternatives for common patterns:
|
|
|
|
| Async (broken in allow/deny) | Sync replacement |
|
|
|------------------------------|------------------|
|
|
| `await ReactiveCache.getBoard(id)` | `Boards.findOne(id)` |
|
|
| `await ReactiveCache.getCard(id)` | `Cards.findOne(id)` |
|
|
| `await ReactiveCache.getCurrentUser()` | `Meteor.users.findOne(userId)` |
|
|
| `await ReactiveCache.getBoards({...})` | `Boards.find({...}).fetch()` |
|
|
| `await card.board()` | `Boards.findOne(card.boardId)` |
|
|
|
|
**Note:** These sync Mongo calls (`findOne`, `find().fetch()`) are available in Meteor 2.x. In Meteor 3.0, they will be replaced by `findOneAsync` / `find().fetchAsync()`, which will require allow/deny callbacks to be reworked again (or replaced by Meteor 3.0's new permission model).
|
|
|
|
---
|
|
|
|
## 5. Server-Only Code CAN Be Async
|
|
|
|
Code that runs exclusively on the server can safely use `async`/`await`:
|
|
|
|
- `Meteor.methods({})` — method bodies
|
|
- `Meteor.publish()` — publication functions
|
|
- `JsonRoutes.add()` — REST API handlers
|
|
- `Collection.before.*` / `Collection.after.*` — collection hooks (via `matb33:collection-hooks`)
|
|
- Standalone server functions
|
|
|
|
```javascript
|
|
Meteor.methods({
|
|
async createCard(data) {
|
|
const board = await ReactiveCache.getBoard(data.boardId); // OK
|
|
// ...
|
|
},
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 6. forEach with await Anti-Pattern
|
|
|
|
`Array.forEach()` does not handle async callbacks — iterations run concurrently without awaiting.
|
|
|
|
```javascript
|
|
// WRONG:
|
|
items.forEach(async (item) => {
|
|
await processItem(item); // Runs all in parallel, not sequentially
|
|
});
|
|
|
|
// CORRECT:
|
|
for (const item of items) {
|
|
await processItem(item); // Runs sequentially
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Client-Side Collection Updates
|
|
|
|
Meteor requires client-side collection updates to use `_id` as the selector:
|
|
|
|
```javascript
|
|
// CORRECT:
|
|
Lists.updateAsync(listId, { $set: { title: newTitle } });
|
|
|
|
// WRONG - fails with "Untrusted code may only update documents by ID":
|
|
Lists.updateAsync({ _id: listId, boardId: boardId }, { $set: { title: newTitle } });
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Sync Meteor 2.x APIs to Convert for 3.0
|
|
|
|
These Meteor 2.x sync APIs will need conversion when upgrading to Meteor 3.0:
|
|
|
|
| Meteor 2.x (sync) | Meteor 3.0 (async) |
|
|
|--------------------|--------------------|
|
|
| `Collection.findOne()` | `Collection.findOneAsync()` |
|
|
| `Collection.find().fetch()` | `Collection.find().fetchAsync()` |
|
|
| `Collection.insert()` | `Collection.insertAsync()` |
|
|
| `Collection.update()` | `Collection.updateAsync()` |
|
|
| `Collection.remove()` | `Collection.removeAsync()` |
|
|
| `Collection.upsert()` | `Collection.upsertAsync()` |
|
|
| `Meteor.user()` | `Meteor.userAsync()` |
|
|
| `Meteor.userId()` | Remains sync |
|
|
|
|
**Current status:** Server-side code already uses async patterns via `ReactiveCache`. The sync `findOne()` calls in allow/deny callbacks will need to be addressed when Meteor 3.0's allow/deny system supports async (or is replaced).
|
|
|
|
---
|
|
|
|
## 9. Files Reference
|
|
|
|
Key files involved in the async migration:
|
|
|
|
| File | Role |
|
|
|------|------|
|
|
| `imports/reactiveCache.js` | ReactiveCache facade + Server/Client/Index implementations |
|
|
| `server/lib/utils.js` | Permission helper functions (`allowIsBoardMember*`) |
|
|
| `models/*.js` | Collection schemas, helpers, allow/deny, hooks, methods |
|
|
| `server/publications/*.js` | Meteor publications |
|
|
| `server/rulesHelper.js` | Rule trigger/action evaluation |
|
|
| `server/cronMigrationManager.js` | Cron-based migration jobs |
|