🐛 fix: replace multi-join $lookup in getPromptGroup for DocumentDB

Replace the subpipeline $lookup in getPromptGroup with the simple
localField/foreignField form and enforce tenant isolation on the joined
production prompt in JS after $unwind.

AWS DocumentDB does not support the $lookup with let + pipeline form
and rejects the aggregation with:

    MongoServerError: Aggregation stage not supported:
    '$lookup on multiple join conditions'

Because the catch block returns null, the route treats the prompt
group as missing and responds 404 Not Found. Any page that loads a
single prompt group (open-for-edit, detail view, etc.) breaks under
TENANT_ISOLATION_STRICT=true on a DocumentDB-backed deployment.

The simple localField/foreignField form works on both real MongoDB
and DocumentDB. Tenant filtering of the joined production prompt is
now done after $unwind by clearing the field when its tenantId does
not match the active context.
This commit is contained in:
Dustin Healy 2026-05-21 07:59:34 -07:00
parent f34150e8e8
commit ea3cb56988

View file

@ -512,37 +512,26 @@ export function createPromptMethods(mongoose: typeof import('mongoose'), deps: P
const tenantId = getTenantId();
const useTenantFilter = tenantId && tenantId !== SYSTEM_TENANT_ID;
const lookupStage = useTenantFilter
? {
$lookup: {
from: 'prompts',
let: { prodId: '$productionId' },
pipeline: [
{
$match: {
$expr: { $eq: ['$_id', '$$prodId'] },
tenantId,
},
},
],
as: 'productionPrompt',
},
}
: {
$lookup: {
from: 'prompts',
localField: 'productionId',
foreignField: '_id',
as: 'productionPrompt',
},
};
const result = await PromptGroup.aggregate([
{ $match: matchFilter },
lookupStage,
{
$lookup: {
from: 'prompts',
localField: 'productionId',
foreignField: '_id',
as: 'productionPrompt',
},
},
{ $unwind: { path: '$productionPrompt', preserveNullAndEmptyArrays: true } },
]);
const group = result[0] || null;
if (
group?.productionPrompt &&
useTenantFilter &&
group.productionPrompt.tenantId !== tenantId
) {
group.productionPrompt = null;
}
if (group?.author) {
group.author = group.author.toString();
}