mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-19 01:17:31 +00:00
fix(sync): treat unreadable legacy __meta_ as legacy detected
The legacy pfapi probe (1f5184f6e7) only mapped
RemoteFileNotFoundAPIError to LegacySyncFormatDetectedError. WebDAV
surfaces a corrupt or empty __meta_ body as InvalidDataSPError, so
those users bypassed the friendly force-overwrite snack and saw an
unhandled data error instead. Presence — even unreadable — still
proves a v16.x client touched the target, so set legacyFileFound on
InvalidDataSPError and let the existing warning path take over.
Also widen the Dropbox and LocalFile getFileRev signatures (and the
Dropbox getMetaData call under it) to accept `string | null`, matching
the interface and the `null` argument the legacy probe already passes.
No runtime behaviour change — Dropbox never serializes the revision
for metadata lookups and LocalFile only logs it.
This commit is contained in:
parent
fddeb581dd
commit
d9158d6adb
5 changed files with 38 additions and 4 deletions
|
|
@ -143,7 +143,7 @@ export class DropboxApi {
|
|||
/**
|
||||
* Retrieve metadata for a file or folder
|
||||
*/
|
||||
async getMetaData(path: string, localRev: string): Promise<DropboxFileMetadata> {
|
||||
async getMetaData(path: string, localRev: string | null): Promise<DropboxFileMetadata> {
|
||||
try {
|
||||
const response = await this._request({
|
||||
method: 'POST',
|
||||
|
|
|
|||
|
|
@ -86,7 +86,10 @@ export class Dropbox implements SyncProviderServiceInterface<SyncProviderId.Drop
|
|||
* @throws RemoteFileNotFoundAPIError if the file doesn't exist
|
||||
* @throws AuthFailSPError if authentication fails
|
||||
*/
|
||||
async getFileRev(targetPath: string, localRev: string): Promise<{ rev: string }> {
|
||||
async getFileRev(
|
||||
targetPath: string,
|
||||
localRev: string | null,
|
||||
): Promise<{ rev: string }> {
|
||||
try {
|
||||
const r = await this._api.getMetaData(this._getPath(targetPath), localRev);
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
SyncDataCorruptedError,
|
||||
} from './file-based-sync.types';
|
||||
import {
|
||||
InvalidDataSPError,
|
||||
RemoteFileNotFoundAPIError,
|
||||
UploadRevToMatchMismatchAPIError,
|
||||
} from '../../core/errors/sync-errors';
|
||||
|
|
@ -1729,6 +1730,24 @@ describe('FileBasedSyncAdapterService', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('throws LegacySyncFormatDetectedError when __meta_ exists but is unreadable (WebDAV corrupt body)', async () => {
|
||||
// Simulate a WebDAV provider with a present-but-corrupt legacy file:
|
||||
// downloadFile throws InvalidDataSPError for the legacy probe, but
|
||||
// the file exists → still proves v16.x touched this target.
|
||||
mockProvider.downloadFile.and.rejectWith(
|
||||
new RemoteFileNotFoundAPIError('not found'),
|
||||
);
|
||||
mockProvider.getFileRev.and.callFake(async (path: string) => {
|
||||
if (path === FILE_BASED_SYNC_CONSTANTS.LEGACY_META_FILE)
|
||||
throw new InvalidDataSPError('empty body', path);
|
||||
throw new RemoteFileNotFoundAPIError('not found');
|
||||
});
|
||||
|
||||
await expectAsync(adapter.downloadOps(0)).toBeRejectedWithError(
|
||||
/Sync format mismatch/,
|
||||
);
|
||||
});
|
||||
|
||||
it('treats missing __meta_ as a genuine fresh start (not an error)', async () => {
|
||||
// Both sync-data.json and __meta_ absent → fresh install
|
||||
mockProvider.downloadFile.and.callFake(async (_path: string) => {
|
||||
|
|
|
|||
|
|
@ -924,7 +924,16 @@ export class FileBasedSyncAdapterService {
|
|||
await provider.getFileRev(FILE_BASED_SYNC_CONSTANTS.LEGACY_META_FILE, null);
|
||||
legacyFileFound = true;
|
||||
} catch (innerE) {
|
||||
if (!(innerE instanceof RemoteFileNotFoundAPIError)) throw innerE;
|
||||
// Why: WebDAV surfaces a corrupt/empty legacy __meta_ body as
|
||||
// InvalidDataSPError (not RemoteFileNotFoundAPIError). The presence
|
||||
// of the file — even if unreadable — still proves a v16.x client
|
||||
// touched this target, so treat it the same as a successful probe
|
||||
// rather than letting the unfriendly InvalidDataSPError escape.
|
||||
if (innerE instanceof InvalidDataSPError) {
|
||||
legacyFileFound = true;
|
||||
} else if (!(innerE instanceof RemoteFileNotFoundAPIError)) {
|
||||
throw innerE;
|
||||
}
|
||||
// __meta_ not found either → genuine fresh start
|
||||
}
|
||||
if (legacyFileFound) throw new LegacySyncFormatDetectedError();
|
||||
|
|
|
|||
|
|
@ -45,7 +45,10 @@ export abstract class LocalFileSyncBase implements SyncProviderServiceInterface<
|
|||
return this.fileAdapter.listFiles(fullPath);
|
||||
}
|
||||
|
||||
async getFileRev(targetPath: string, localRev: string): Promise<{ rev: string }> {
|
||||
async getFileRev(
|
||||
targetPath: string,
|
||||
localRev: string | null,
|
||||
): Promise<{ rev: string }> {
|
||||
SyncLog.normal(`${LocalFileSyncBase.LB}.${this.getFileRev.name}`, {
|
||||
targetPath,
|
||||
localRev,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue