feat(listings): allow admin to PATCH /listings/:id (TEC-2746)
- UpdateListingCommand accepts userRole; ADMIN bypasses owner/agent check - Controller forwards user.role from JwtPayload - Adds unit test covering admin-authorized edit path Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -137,7 +137,30 @@ describe('UpdateListingHandler', () => {
|
|||||||
|
|
||||||
const command = new UpdateListingCommand('listing-1', 'stranger', 'Tiêu đề mới');
|
const command = new UpdateListingCommand('listing-1', 'stranger', 'Tiêu đề mới');
|
||||||
|
|
||||||
await expect(handler.execute(command)).rejects.toThrow(/người bán|môi giới/);
|
await expect(handler.execute(command)).rejects.toThrow(/người bán|môi giới|quản trị/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows an admin to update any listing', async () => {
|
||||||
|
const listing = createListing('listing-1', 'seller-1');
|
||||||
|
const property = createProperty('prop-1');
|
||||||
|
mockListingRepo.findById.mockResolvedValue(listing);
|
||||||
|
mockPropertyRepo.findById.mockResolvedValue(property);
|
||||||
|
|
||||||
|
const command = new UpdateListingCommand(
|
||||||
|
'listing-1',
|
||||||
|
'admin-user',
|
||||||
|
'Tiêu đề do admin sửa',
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
'ADMIN',
|
||||||
|
);
|
||||||
|
const result = await handler.execute(command);
|
||||||
|
|
||||||
|
expect(result.listingId).toBe('listing-1');
|
||||||
|
expect(result.updatedFields).toContain('title');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8,5 +8,6 @@ export class UpdateListingCommand {
|
|||||||
public readonly rentPriceMonthly?: bigint,
|
public readonly rentPriceMonthly?: bigint,
|
||||||
public readonly amenities?: string[],
|
public readonly amenities?: string[],
|
||||||
public readonly mediaOrder?: { mediaId: string; order: number }[],
|
public readonly mediaOrder?: { mediaId: string; order: number }[],
|
||||||
|
public readonly userRole?: string,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,12 +38,13 @@ export class UpdateListingHandler implements ICommandHandler<UpdateListingComman
|
|||||||
throw new NotFoundException('Listing', command.listingId);
|
throw new NotFoundException('Listing', command.listingId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Ownership check: only the seller or assigned agent can edit
|
// 2. Ownership check: only the seller, assigned agent, or admin can edit
|
||||||
const isOwner = listing.sellerId === command.userId;
|
const isOwner = listing.sellerId === command.userId;
|
||||||
const isAgent = listing.agentId !== null && listing.agentId === command.userId;
|
const isAgent = listing.agentId !== null && listing.agentId === command.userId;
|
||||||
if (!isOwner && !isAgent) {
|
const isAdmin = command.userRole === 'ADMIN';
|
||||||
|
if (!isOwner && !isAgent && !isAdmin) {
|
||||||
throw new ForbiddenException(
|
throw new ForbiddenException(
|
||||||
'Chỉ người bán hoặc môi giới được giao mới có thể chỉnh sửa tin đăng',
|
'Chỉ người bán, môi giới được giao hoặc quản trị viên mới có thể chỉnh sửa tin đăng',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -235,6 +235,7 @@ export class ListingsController {
|
|||||||
dto.rentPriceMonthly,
|
dto.rentPriceMonthly,
|
||||||
dto.amenities,
|
dto.amenities,
|
||||||
dto.mediaOrder,
|
dto.mediaOrder,
|
||||||
|
user.role,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user