fix(api): stop one unreachable community from breaking bookmarks (#1779)

This commit is contained in:
Hampus authored and GitHub committed 2026-08-20 15:50:51 +02:00
1 parent 47b5c3d4f0
commit 528777926c
2 files changed
+40 -6

No files matched your search

@@ -0,0 +1,30 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {UnknownChannelError} from '@fluxer/errors/src/domains/channel/UnknownChannelError';
import {UnknownMessageError} from '@fluxer/errors/src/domains/channel/UnknownMessageError';
import {MissingPermissionsError} from '@fluxer/errors/src/domains/core/MissingPermissionsError';
import {UnknownGuildError} from '@fluxer/errors/src/domains/guild/UnknownGuildError';
import {describe, expect, it} from 'vitest';
import {UserContentServiceTestHooks} from './UserContentService';
const {isUnreachableEntityError} = UserContentServiceTestHooks;
describe('isUnreachableEntityError', () => {
it('treats a deleted or left community as unreachable rather than fatal', () => {
expect(isUnreachableEntityError(new UnknownGuildError())).toBe(true);
});
it('treats a gone channel and a lost permission as unreachable', () => {
expect(isUnreachableEntityError(new UnknownChannelError())).toBe(true);
expect(isUnreachableEntityError(new MissingPermissionsError())).toBe(true);
});
it('leaves a deleted message to the delete path instead of marking it unavailable', () => {
expect(isUnreachableEntityError(new UnknownMessageError())).toBe(false);
});
it('still lets unexpected failures surface', () => {
expect(isUnreachableEntityError(new Error('database is on fire'))).toBe(false);
expect(isUnreachableEntityError(null)).toBe(false);
});
});
@@ -6,6 +6,7 @@ import {UnknownChannelError} from '@fluxer/errors/src/domains/channel/UnknownCha
import {UnknownMessageError} from '@fluxer/errors/src/domains/channel/UnknownMessageError';
import {MaxBookmarksError} from '@fluxer/errors/src/domains/core/MaxBookmarksError';
import {MissingPermissionsError} from '@fluxer/errors/src/domains/core/MissingPermissionsError';
import {UnknownGuildError} from '@fluxer/errors/src/domains/guild/UnknownGuildError';
import {HarvestExpiredError} from '@fluxer/errors/src/domains/moderation/HarvestExpiredError';
import {HarvestFailedError} from '@fluxer/errors/src/domains/moderation/HarvestFailedError';
import {HarvestNotReadyError} from '@fluxer/errors/src/domains/moderation/HarvestNotReadyError';
@@ -94,6 +95,13 @@ function normalizeProviderEnvironment(
return platform === 'ios_apns' ? DEFAULT_APNS_PROVIDER_ENVIRONMENT : null;
}
const isUnreachableEntityError = (error: unknown): boolean =>
error instanceof MissingPermissionsError ||
error instanceof UnknownChannelError ||
error instanceof UnknownGuildError;
export const UserContentServiceTestHooks = {isUnreachableEntityError};
export class UserContentService {
private readonly updatePropagator: BaseUserUpdatePropagator;
private readonly userRepository: UserContentRepository;
@@ -138,11 +146,7 @@ export class UserContentService {
messageId: mention.messageId,
});
} catch (error) {
if (
error instanceof UnknownMessageError ||
error instanceof MissingPermissionsError ||
error instanceof UnknownChannelError
) {
if (error instanceof UnknownMessageError || isUnreachableEntityError(error)) {
return null;
}
throw error;
@@ -188,7 +192,7 @@ export class UserContentService {
await this.userRepository.deleteSavedMessage(userId, savedMessage.messageId);
return null;
}
if (error instanceof MissingPermissionsError || error instanceof UnknownChannelError) {
if (isUnreachableEntityError(error)) {
status = 'missing_permissions';
} else {
throw error;