Skip to content

Commit 1002002

Browse files
Get placeholder states
1 parent 652ae16 commit 1002002

File tree

8 files changed

+110
-84
lines changed

8 files changed

+110
-84
lines changed

examples/callbacks/notify-fetch-data.callback.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { getInfoItem } from "examples/info-items-manager";
22

3+
import { TFetchDataCallback } from "@/types/callbacks.type";
34
import { sleep } from "@/utils";
45

5-
type TCallback = (data: boolean, path: string, errorHandler?: () => void) => Promise<{ finished: boolean; progress: number }>;
6-
7-
export const fetchDataCallback = async (id: string, callback: TCallback) => {
6+
export const fetchDataCallback = async (id: string, callback: Parameters<TFetchDataCallback>[1]) => {
87
const path = await getInfoItem(id);
98

109
let finish = false;

examples/populate.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import { v4 } from "uuid";
66

77
import settings from "./settings";
88

9-
const rootFile1 = join(settings.syncRootPath, v4());
9+
const rootFileName1 = v4();
10+
const rootZipFileName = `${v4()}.zip`;
11+
const rootFile1 = join(settings.syncRootPath, rootFileName1);
1012
const rootFile2ChangeSize = join(settings.syncRootPath, `change-size-${v4()}.txt`);
1113
const rootFile3 = join(settings.syncRootPath, `${v4()}.txt`);
1214
const rootFile3Moved = join(settings.syncRootPath, `moved-${v4()}.txt`);
@@ -22,6 +24,7 @@ execSync(`echo Hello, world! > ${rootFile2ChangeSize}`);
2224
execSync(`echo Hello, world! >> ${rootFile2ChangeSize}`); // Sync
2325
execSync(`echo Hello, world! > ${rootFile3}`);
2426
execSync(`type nul > ${rootFile4}`); // No sync (0 bytes)
27+
execSync(`cd ${settings.syncRootPath} && tar -cf ${rootZipFileName} ${rootFileName1}`); // Sync
2528
execSync(`mv ${rootFile3} ${rootFile3Moved}`); // Sync
2629
execSync(`mkdir ${rootFolder1}`); // Sync
2730
execSync(`mkdir ${rootFolder2}`); // Cloud (no files inside)

examples/register.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ const handlers = { handleAdd, handleHydrate, handleDehydrate, handleChangeSize }
2020
const notify = { onTaskSuccess: async () => undefined, onTaskProcessing: async () => undefined };
2121
const queueManager = new QueueManager(handlers, notify, settings.queuePersistPath);
2222

23-
drive.registerSyncRoot(settings.driveName, settings.driveVersion, settings.providerid, callbacks, settings.iconPath);
24-
drive.connectSyncRoot();
25-
26-
try {
27-
initInfoItems();
28-
drive.watchAndWait(settings.syncRootPath, queueManager, settings.watcherLogPath);
29-
} catch (error) {
30-
logger.error(error);
31-
drive.disconnectSyncRoot();
32-
VirtualDrive.unregisterSyncRoot(settings.syncRootPath);
33-
}
23+
drive.registerSyncRoot(settings.driveName, settings.driveVersion, settings.providerid, callbacks, settings.iconPath).then(() => {
24+
drive.connectSyncRoot();
25+
26+
try {
27+
initInfoItems();
28+
drive.watchAndWait(settings.syncRootPath, queueManager, settings.watcherLogPath);
29+
} catch (error) {
30+
logger.error(error);
31+
drive.disconnectSyncRoot();
32+
VirtualDrive.unregisterSyncRoot(settings.syncRootPath);
33+
}
34+
});

src/get-placeholder-states.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { readdir } from "fs/promises";
2+
import { join } from "path";
3+
4+
import { isFileInDevice } from "./is-file-in-device";
5+
import VirtualDrive from "./virtual-drive";
6+
7+
type TProps = {
8+
self: VirtualDrive;
9+
path: string;
10+
};
11+
12+
export const getPlaceholderStates = async ({ self, path }: TProps) => {
13+
const files = await readdir(path, { withFileTypes: true });
14+
15+
const promises = files.map(async (file) => {
16+
const filePath = join(path, file.name);
17+
18+
if (file.isDirectory()) {
19+
return getPlaceholderStates({ self, path: filePath });
20+
} else {
21+
const status = self.getPlaceholderState(filePath);
22+
if (isFileInDevice(status)) {
23+
const id = self.getFileIdentity(filePath);
24+
self.watcher.fileInDevice.add(id);
25+
}
26+
}
27+
});
28+
29+
await Promise.all(promises);
30+
};

src/is-file-in-device.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { PinState, SyncState } from "./types/placeholder.type";
2+
3+
type TProps = {
4+
syncState: SyncState;
5+
pinState: PinState;
6+
};
7+
8+
export const isFileInDevice = ({ syncState, pinState }: TProps) => {
9+
const inSync = syncState === SyncState.InSync;
10+
const isHydrated = pinState === PinState.AlwaysLocal || pinState === PinState.Unspecified;
11+
return inSync && isHydrated;
12+
};

src/types/callbacks.type.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
export type NapiCallbackFunction = (...args: any[]) => any;
22

3+
export type TFetchDataCallback = (
4+
id: string,
5+
callback: (data: boolean, path: string, errorHandler?: () => void) => Promise<{ finished: boolean; progress: number }>,
6+
) => void;
7+
38
export type InputSyncCallbacks = {
4-
// TODO: make it mandatory and add the exact type
5-
fetchDataCallback?: NapiCallbackFunction;
9+
fetchDataCallback: TFetchDataCallback;
610
validateDataCallback?: NapiCallbackFunction;
711
cancelFetchDataCallback?: NapiCallbackFunction;
812
fetchPlaceholdersCallback?: NapiCallbackFunction;

src/virtual-drive.ts

Lines changed: 39 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,40 @@
11
import path, { join, win32 } from "path";
22
import fs from "fs";
33
import { Watcher } from "./watcher/watcher";
4-
import { ExtraCallbacks, InputSyncCallbacks } from "./types/callbacks.type";
4+
import { Callbacks } from "./types/callbacks.type";
55
import { Status } from "./types/placeholder.type";
66
import { IQueueManager } from "./queue/queueManager";
77

88
import { createLogger } from "./logger";
99
import { Addon } from "./addon-wrapper";
10+
import { getPlaceholderStates } from "./get-placeholder-states";
11+
import winston from "winston";
1012

1113
const addon = new Addon();
1214

13-
type Callbacks = InputSyncCallbacks & ExtraCallbacks;
15+
const PLACEHOLDER_ATTRIBUTES = {
16+
FILE_ATTRIBUTE_READONLY: 0x1,
17+
FILE_ATTRIBUTE_HIDDEN: 0x2,
18+
FOLDER_ATTRIBUTE_READONLY: 0x1,
19+
FILE_ATTRIBUTE_NORMAL: 0x1,
20+
};
21+
1422
class VirtualDrive {
15-
PLACEHOLDER_ATTRIBUTES: { [key: string]: number };
1623
syncRootPath: string;
17-
callbacks?: Callbacks;
18-
19-
// private watcherBuilder: WatcherBuilder;
20-
private watcher: Watcher;
21-
22-
constructor(syncRootPath: string, loggerPath?: string) {
23-
this.PLACEHOLDER_ATTRIBUTES = {
24-
FILE_ATTRIBUTE_READONLY: 0x1,
25-
FILE_ATTRIBUTE_HIDDEN: 0x2,
26-
FOLDER_ATTRIBUTE_READONLY: 0x1,
27-
FILE_ATTRIBUTE_NORMAL: 0x1,
28-
};
24+
callbacks!: Callbacks;
25+
watcher = new Watcher();
26+
logger: winston.Logger;
2927

28+
constructor(syncRootPath: string, loggerPath: string) {
3029
// TODO: getPlaceholderStates in the beginning
3130

3231
this.watcher = new Watcher();
3332

34-
this.syncRootPath = syncRootPath;
33+
this.syncRootPath = this.convertToWindowsPath(syncRootPath);
34+
loggerPath = this.convertToWindowsPath(loggerPath);
3535
this.createSyncRootFolder();
36-
37-
let pathElements = this.syncRootPath.split("\\\\");
38-
pathElements.pop();
39-
let parentPath = pathElements.join("\\\\");
40-
41-
this.addLoggerPath(loggerPath ?? parentPath);
36+
this.addLoggerPath(loggerPath);
37+
this.logger = createLogger(loggerPath);
4238
}
4339

4440
addLoggerPath(logPath: string) {
@@ -66,39 +62,6 @@ class VirtualDrive {
6662
return addon.getPlaceholderWithStatePending();
6763
}
6864

69-
getInputSyncCallbacks(): InputSyncCallbacks {
70-
if (this.callbacks === undefined) {
71-
throw new Error("Callbacks are not defined");
72-
}
73-
74-
const inputSyncCallbackKeys: (keyof InputSyncCallbacks)[] = [
75-
"fetchDataCallback",
76-
"validateDataCallback",
77-
"cancelFetchDataCallback",
78-
"fetchPlaceholdersCallback",
79-
"cancelFetchPlaceholdersCallback",
80-
"notifyFileOpenCompletionCallback",
81-
"notifyFileCloseCompletionCallback",
82-
"notifyDehydrateCallback",
83-
"notifyDehydrateCompletionCallback",
84-
"notifyDeleteCallback",
85-
"notifyDeleteCompletionCallback",
86-
"notifyRenameCallback",
87-
"notifyRenameCompletionCallback",
88-
"noneCallback",
89-
];
90-
91-
const result: InputSyncCallbacks = {};
92-
93-
for (const key of inputSyncCallbackKeys) {
94-
if (this.callbacks[key] !== undefined) {
95-
result[key] = this.callbacks[key];
96-
}
97-
}
98-
99-
return result;
100-
}
101-
10265
createSyncRootFolder() {
10366
if (!fs.existsSync(this.syncRootPath)) {
10467
fs.mkdirSync(this.syncRootPath, { recursive: true });
@@ -117,8 +80,12 @@ class VirtualDrive {
11780
return addon.deleteFileSyncRoot({ path: this.fixPath(relativePath) });
11881
}
11982

120-
async connectSyncRoot(): Promise<any> {
121-
return addon.connectSyncRoot({ callbacks: this.getInputSyncCallbacks() });
83+
async connectSyncRoot() {
84+
if (this.callbacks === undefined) {
85+
throw new Error("Callbacks are not defined");
86+
}
87+
88+
return addon.connectSyncRoot({ callbacks: this.callbacks });
12289
}
12390

12491
createPlaceholderFile(
@@ -193,11 +160,18 @@ class VirtualDrive {
193160
this.callbacks = {
194161
...callbacks,
195162
fetchDataCallback: (...args) => {
196-
const path = args[0];
197-
this.watcher.fileInDevice.add(path);
163+
const id = args[0];
164+
this.watcher.fileInDevice.add(id);
198165
return callbacks.fetchDataCallback?.(...args);
199166
}
200167
};
168+
169+
try {
170+
await getPlaceholderStates({ self: this, path: this.syncRootPath});
171+
} catch (exc) {
172+
this.logger.error("getPlaceholderStates", exc)
173+
}
174+
201175
return addon.registerSyncRoot({
202176
syncRootPath: this.syncRootPath,
203177
providerName,
@@ -227,7 +201,7 @@ class VirtualDrive {
227201

228202
this.watcher.queueManager = queueManager;
229203

230-
this.watcher.logger = createLogger(loggerPath);
204+
this.watcher.logger = this.logger;
231205

232206
this.watcher.syncRootPath = path;
233207
this.watcher.options = {
@@ -271,7 +245,7 @@ class VirtualDrive {
271245
path.basename(fullPath),
272246
itemId,
273247
size,
274-
this.PLACEHOLDER_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL,
248+
PLACEHOLDER_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL,
275249
creationTime,
276250
lastWriteTime,
277251
Date.now(),
@@ -304,7 +278,7 @@ class VirtualDrive {
304278
itemId,
305279
true,
306280
size,
307-
this.PLACEHOLDER_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL,
281+
PLACEHOLDER_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL,
308282
creationTime,
309283
lastWriteTime,
310284
Date.now(),
@@ -338,7 +312,7 @@ class VirtualDrive {
338312
itemId,
339313
true,
340314
size,
341-
this.PLACEHOLDER_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL,
315+
PLACEHOLDER_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL,
342316
creationTime,
343317
lastWriteTime,
344318
Date.now(),
@@ -364,7 +338,7 @@ class VirtualDrive {
364338
itemId,
365339
true,
366340
0,
367-
this.PLACEHOLDER_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL,
341+
PLACEHOLDER_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL,
368342
Date.now(),
369343
Date.now(),
370344
Date.now(),
@@ -378,7 +352,7 @@ class VirtualDrive {
378352
path.basename(fullPath),
379353
itemId,
380354
size,
381-
this.PLACEHOLDER_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL,
355+
PLACEHOLDER_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL,
382356
creationTime,
383357
lastWriteTime,
384358
Date.now(),

src/virtual-drive.unit.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import fs from "fs";
22
import { v4 } from "uuid";
33
import { Mock } from "vitest";
4+
import { mockDeep } from "vitest-mock-extended";
45

56
import { addon } from "@/addon";
67

8+
import { Callbacks } from "./types/callbacks.type";
79
import VirtualDrive from "./virtual-drive";
810

911
vi.mock("fs");
@@ -138,13 +140,14 @@ describe("VirtualDrive", () => {
138140
const providerVersion = "1.0.0";
139141
const providerId = v4();
140142
const logoPath = "C:\\iconPath";
141-
const callbacks = {};
143+
const callbacks = mockDeep<Callbacks>();
142144

143145
// Act
146+
expect(drive.callbacks).toBe(undefined);
144147
await drive.registerSyncRoot(providerName, providerVersion, providerId, callbacks, logoPath);
145148

146149
// Assert
147-
expect(drive.callbacks).toBe(callbacks);
150+
expect(drive.callbacks).not.toBe(undefined);
148151
expect(addon.registerSyncRoot).toHaveBeenCalledWith(syncRootPath, providerName, providerVersion, providerId, logoPath);
149152
});
150153
});

0 commit comments

Comments
 (0)