@protontech/drive-sdk 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/diagnostic/diagnostic.d.ts +7 -4
- package/dist/diagnostic/diagnostic.js +16 -8
- package/dist/diagnostic/diagnostic.js.map +1 -1
- package/dist/diagnostic/index.d.ts +1 -1
- package/dist/diagnostic/index.js +9 -1
- package/dist/diagnostic/index.js.map +1 -1
- package/dist/diagnostic/interface.d.ts +24 -9
- package/dist/diagnostic/nodeUtils.d.ts +13 -0
- package/dist/diagnostic/nodeUtils.js +90 -0
- package/dist/diagnostic/nodeUtils.js.map +1 -0
- package/dist/diagnostic/sdkDiagnosticBase.d.ts +36 -0
- package/dist/diagnostic/sdkDiagnosticBase.js +305 -0
- package/dist/diagnostic/sdkDiagnosticBase.js.map +1 -0
- package/dist/diagnostic/sdkDiagnosticMain.d.ts +16 -0
- package/dist/diagnostic/sdkDiagnosticMain.js +79 -0
- package/dist/diagnostic/sdkDiagnosticMain.js.map +1 -0
- package/dist/diagnostic/sdkDiagnosticPhotos.d.ts +13 -0
- package/dist/diagnostic/sdkDiagnosticPhotos.js +65 -0
- package/dist/diagnostic/sdkDiagnosticPhotos.js.map +1 -0
- package/dist/internal/devices/interface.d.ts +1 -1
- package/dist/internal/devices/manager.js +1 -1
- package/dist/internal/devices/manager.js.map +1 -1
- package/dist/internal/devices/manager.test.js +3 -3
- package/dist/internal/devices/manager.test.js.map +1 -1
- package/dist/internal/errors.d.ts +5 -0
- package/dist/internal/errors.js +23 -0
- package/dist/internal/errors.js.map +1 -1
- package/dist/internal/errors.test.js +53 -2
- package/dist/internal/errors.test.js.map +1 -1
- package/dist/internal/nodes/cryptoReporter.js +3 -0
- package/dist/internal/nodes/cryptoReporter.js.map +1 -1
- package/dist/internal/nodes/index.test.js +1 -1
- package/dist/internal/nodes/index.test.js.map +1 -1
- package/dist/internal/nodes/interface.d.ts +1 -1
- package/dist/internal/nodes/nodesAccess.d.ts +1 -1
- package/dist/internal/nodes/nodesAccess.js +4 -4
- package/dist/internal/nodes/nodesAccess.js.map +1 -1
- package/dist/internal/nodes/nodesAccess.test.js +2 -2
- package/dist/internal/nodes/nodesAccess.test.js.map +1 -1
- package/dist/internal/photos/albums.js +1 -1
- package/dist/internal/photos/albums.js.map +1 -1
- package/dist/internal/photos/apiService.d.ts +6 -0
- package/dist/internal/photos/apiService.js +16 -0
- package/dist/internal/photos/apiService.js.map +1 -1
- package/dist/internal/photos/index.d.ts +1 -1
- package/dist/internal/photos/index.js +2 -2
- package/dist/internal/photos/index.js.map +1 -1
- package/dist/internal/photos/interface.d.ts +4 -1
- package/dist/internal/photos/shares.d.ts +1 -1
- package/dist/internal/photos/shares.js +3 -3
- package/dist/internal/photos/shares.js.map +1 -1
- package/dist/internal/photos/timeline.d.ts +8 -1
- package/dist/internal/photos/timeline.js +36 -2
- package/dist/internal/photos/timeline.js.map +1 -1
- package/dist/internal/photos/timeline.test.d.ts +1 -0
- package/dist/internal/photos/timeline.test.js +99 -0
- package/dist/internal/photos/timeline.test.js.map +1 -0
- package/dist/internal/shares/cryptoService.js +3 -0
- package/dist/internal/shares/cryptoService.js.map +1 -1
- package/dist/internal/shares/manager.d.ts +1 -1
- package/dist/internal/shares/manager.js +4 -4
- package/dist/internal/shares/manager.js.map +1 -1
- package/dist/internal/shares/manager.test.js +7 -7
- package/dist/internal/shares/manager.test.js.map +1 -1
- package/dist/internal/sharing/interface.d.ts +1 -1
- package/dist/internal/sharing/sharingAccess.js +1 -1
- package/dist/internal/sharing/sharingAccess.js.map +1 -1
- package/dist/internal/sharing/sharingAccess.test.js +1 -1
- package/dist/internal/sharing/sharingAccess.test.js.map +1 -1
- package/dist/internal/sharing/sharingManagement.js +32 -14
- package/dist/internal/sharing/sharingManagement.js.map +1 -1
- package/dist/internal/sharing/sharingManagement.test.js +46 -1
- package/dist/internal/sharing/sharingManagement.test.js.map +1 -1
- package/dist/internal/sharingPublic/cryptoReporter.js +3 -0
- package/dist/internal/sharingPublic/cryptoReporter.js.map +1 -1
- package/dist/internal/sharingPublic/index.d.ts +3 -0
- package/dist/internal/sharingPublic/index.js +3 -0
- package/dist/internal/sharingPublic/index.js.map +1 -1
- package/dist/internal/sharingPublic/shares.d.ts +1 -1
- package/dist/internal/sharingPublic/shares.js +1 -2
- package/dist/internal/sharingPublic/shares.js.map +1 -1
- package/dist/protonDrivePhotosClient.d.ts +19 -0
- package/dist/protonDrivePhotosClient.js +23 -1
- package/dist/protonDrivePhotosClient.js.map +1 -1
- package/dist/protonDrivePublicLinkClient.d.ts +33 -1
- package/dist/protonDrivePublicLinkClient.js +51 -2
- package/dist/protonDrivePublicLinkClient.js.map +1 -1
- package/package.json +1 -1
- package/src/diagnostic/diagnostic.ts +27 -8
- package/src/diagnostic/index.ts +17 -2
- package/src/diagnostic/interface.ts +35 -9
- package/src/diagnostic/nodeUtils.ts +100 -0
- package/src/diagnostic/{sdkDiagnostic.ts → sdkDiagnosticBase.ts} +204 -204
- package/src/diagnostic/sdkDiagnosticMain.ts +95 -0
- package/src/diagnostic/sdkDiagnosticPhotos.ts +70 -0
- package/src/internal/devices/interface.ts +1 -1
- package/src/internal/devices/manager.test.ts +3 -3
- package/src/internal/devices/manager.ts +1 -1
- package/src/internal/errors.test.ts +62 -1
- package/src/internal/errors.ts +27 -0
- package/src/internal/nodes/cryptoReporter.ts +6 -5
- package/src/internal/nodes/index.test.ts +1 -1
- package/src/internal/nodes/interface.ts +1 -1
- package/src/internal/nodes/nodesAccess.test.ts +2 -2
- package/src/internal/nodes/nodesAccess.ts +5 -5
- package/src/internal/photos/albums.ts +1 -1
- package/src/internal/photos/apiService.ts +40 -0
- package/src/internal/photos/index.ts +9 -1
- package/src/internal/photos/interface.ts +4 -1
- package/src/internal/photos/shares.ts +3 -3
- package/src/internal/photos/timeline.test.ts +116 -0
- package/src/internal/photos/timeline.ts +47 -2
- package/src/internal/shares/cryptoService.ts +5 -1
- package/src/internal/shares/manager.test.ts +7 -7
- package/src/internal/shares/manager.ts +4 -4
- package/src/internal/sharing/interface.ts +1 -1
- package/src/internal/sharing/sharingAccess.test.ts +1 -1
- package/src/internal/sharing/sharingAccess.ts +1 -1
- package/src/internal/sharing/sharingManagement.test.ts +59 -1
- package/src/internal/sharing/sharingManagement.ts +33 -14
- package/src/internal/sharingPublic/cryptoReporter.ts +5 -1
- package/src/internal/sharingPublic/index.ts +3 -0
- package/src/internal/sharingPublic/shares.ts +1 -2
- package/src/protonDrivePhotosClient.ts +24 -1
- package/src/protonDrivePublicLinkClient.ts +77 -2
- package/dist/diagnostic/sdkDiagnostic.d.ts +0 -23
- package/dist/diagnostic/sdkDiagnostic.js +0 -320
- package/dist/diagnostic/sdkDiagnostic.js.map +0 -1
|
@@ -1,48 +1,129 @@
|
|
|
1
|
-
import { Author, FileDownloader, MaybeNode,
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { Author, FileDownloader, MaybeNode, NodeOrUid, NodeType, ThumbnailType, ThumbnailResult } from '../interface';
|
|
2
|
+
import {
|
|
3
|
+
DiagnosticOptions,
|
|
4
|
+
DiagnosticResult,
|
|
5
|
+
ExpectedTreeNode,
|
|
6
|
+
DiagnosticProgressCallback,
|
|
7
|
+
} from './interface';
|
|
4
8
|
import { IntegrityVerificationStream } from './integrityVerificationStream';
|
|
9
|
+
import {
|
|
10
|
+
getNodeType,
|
|
11
|
+
getNodeDetails,
|
|
12
|
+
getActiveRevision,
|
|
13
|
+
getMediaType,
|
|
14
|
+
getExpectedTreeNodeDetails,
|
|
15
|
+
getNodeName,
|
|
16
|
+
} from './nodeUtils';
|
|
17
|
+
|
|
18
|
+
const PROGRESS_REPORT_INTERVAL = 500;
|
|
19
|
+
|
|
20
|
+
interface SDKClient {
|
|
21
|
+
getFileDownloader(nodeOrUid: NodeOrUid): Promise<FileDownloader>;
|
|
22
|
+
iterateThumbnails(nodeUids: string[], thumbnailType: ThumbnailType): AsyncGenerator<ThumbnailResult>;
|
|
23
|
+
}
|
|
5
24
|
|
|
6
25
|
/**
|
|
7
|
-
*
|
|
8
|
-
* the
|
|
9
|
-
*
|
|
10
|
-
* It produces only events that can be read by direct SDK invocation.
|
|
11
|
-
* To get the full diagnostic, use {@link FullSDKDiagnostic}.
|
|
26
|
+
* Base class for all SDK diagnostic tools that verifies the integrity of
|
|
27
|
+
* the individual nodes.
|
|
12
28
|
*/
|
|
13
|
-
export class
|
|
14
|
-
|
|
15
|
-
|
|
29
|
+
export class SDKDiagnosticBase {
|
|
30
|
+
private options: Pick<DiagnosticOptions, 'verifyContent' | 'verifyThumbnails'>;
|
|
31
|
+
|
|
32
|
+
private onProgress?: DiagnosticProgressCallback;
|
|
33
|
+
private progressReportInterval: NodeJS.Timeout | undefined;
|
|
34
|
+
|
|
35
|
+
protected nodesQueue: { node: MaybeNode; expected?: ExpectedTreeNode }[] = [];
|
|
36
|
+
protected allNodesLoaded: boolean = false;
|
|
37
|
+
protected loadedNodes: number = 0;
|
|
38
|
+
protected checkedNodes: number = 0;
|
|
39
|
+
|
|
40
|
+
constructor(
|
|
41
|
+
private sdkClient: SDKClient,
|
|
42
|
+
options?: Pick<DiagnosticOptions, 'verifyContent' | 'verifyThumbnails'>,
|
|
43
|
+
onProgress?: DiagnosticProgressCallback,
|
|
44
|
+
) {
|
|
45
|
+
this.sdkClient = sdkClient;
|
|
46
|
+
this.options = options || { verifyContent: false, verifyThumbnails: false };
|
|
47
|
+
this.onProgress = onProgress;
|
|
16
48
|
}
|
|
17
49
|
|
|
18
|
-
|
|
19
|
-
|
|
50
|
+
protected startProgress(): void {
|
|
51
|
+
this.allNodesLoaded = false;
|
|
52
|
+
this.loadedNodes = 0;
|
|
53
|
+
this.checkedNodes = 0;
|
|
20
54
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
55
|
+
this.reportProgress();
|
|
56
|
+
this.progressReportInterval = setInterval(() => {
|
|
57
|
+
this.reportProgress();
|
|
58
|
+
}, PROGRESS_REPORT_INTERVAL);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
protected finishProgress(): void {
|
|
62
|
+
if (this.progressReportInterval) {
|
|
63
|
+
clearInterval(this.progressReportInterval);
|
|
64
|
+
this.progressReportInterval = undefined;
|
|
30
65
|
}
|
|
31
66
|
|
|
32
|
-
|
|
67
|
+
this.reportProgress();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private reportProgress(): void {
|
|
71
|
+
this.onProgress?.({
|
|
72
|
+
allNodesLoaded: this.allNodesLoaded,
|
|
73
|
+
loadedNodes: this.loadedNodes,
|
|
74
|
+
checkedNodes: this.checkedNodes,
|
|
75
|
+
});
|
|
33
76
|
}
|
|
34
77
|
|
|
35
|
-
async *
|
|
36
|
-
|
|
78
|
+
protected async *verifyExpectedNodeChildren(
|
|
79
|
+
parentNodeUid: string,
|
|
80
|
+
children: MaybeNode[],
|
|
81
|
+
expectedStructure?: ExpectedTreeNode,
|
|
82
|
+
): AsyncGenerator<DiagnosticResult> {
|
|
83
|
+
if (!expectedStructure) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const expectedNodes = expectedStructure.children ?? [];
|
|
88
|
+
const actualNodeNames = children.map((child) => getNodeName(child));
|
|
89
|
+
|
|
90
|
+
for (const expectedNode of expectedNodes) {
|
|
91
|
+
if (!actualNodeNames.includes(expectedNode.name)) {
|
|
92
|
+
yield {
|
|
93
|
+
type: 'expected_structure_missing_node',
|
|
94
|
+
expectedNode: getExpectedTreeNodeDetails(expectedNode),
|
|
95
|
+
parentNodeUid,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
37
99
|
|
|
38
|
-
|
|
100
|
+
for (const child of children) {
|
|
101
|
+
const childName = getNodeName(child);
|
|
102
|
+
const isExpected = expectedNodes.some((expectedNode) => expectedNode.name === childName);
|
|
39
103
|
|
|
40
|
-
|
|
41
|
-
|
|
104
|
+
if (!isExpected) {
|
|
105
|
+
yield {
|
|
106
|
+
type: 'expected_structure_unexpected_node',
|
|
107
|
+
...getNodeDetails(child),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
42
110
|
}
|
|
43
111
|
}
|
|
44
112
|
|
|
45
|
-
|
|
113
|
+
protected async *verifyNodesQueue(): AsyncGenerator<DiagnosticResult> {
|
|
114
|
+
while (this.nodesQueue.length > 0 || !this.allNodesLoaded) {
|
|
115
|
+
const result = this.nodesQueue.shift();
|
|
116
|
+
if (result) {
|
|
117
|
+
yield* this.verifyNode(result.node, result.expected);
|
|
118
|
+
this.checkedNodes++;
|
|
119
|
+
} else {
|
|
120
|
+
// Wait for 100ms before checking again.
|
|
121
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private async *verifyNode(node: MaybeNode, expectedStructure?: ExpectedTreeNode): AsyncGenerator<DiagnosticResult> {
|
|
46
127
|
if (!node.ok) {
|
|
47
128
|
yield {
|
|
48
129
|
type: 'degraded_node',
|
|
@@ -50,25 +131,48 @@ export class SDKDiagnostic {
|
|
|
50
131
|
};
|
|
51
132
|
}
|
|
52
133
|
|
|
53
|
-
yield* this.verifyAuthor(node.ok ? node.value.keyAuthor : node.error.keyAuthor, 'key', node);
|
|
54
|
-
yield* this.verifyAuthor(
|
|
134
|
+
yield* this.verifyAuthor(node.ok ? node.value.keyAuthor : node.error.keyAuthor, 'key', node, expectedStructure);
|
|
135
|
+
yield* this.verifyAuthor(
|
|
136
|
+
node.ok ? node.value.nameAuthor : node.error.nameAuthor,
|
|
137
|
+
'name',
|
|
138
|
+
node,
|
|
139
|
+
expectedStructure,
|
|
140
|
+
);
|
|
55
141
|
|
|
56
142
|
const activeRevision = getActiveRevision(node);
|
|
57
143
|
if (activeRevision) {
|
|
58
|
-
yield* this.verifyAuthor(activeRevision.contentAuthor, 'content', node);
|
|
144
|
+
yield* this.verifyAuthor(activeRevision.contentAuthor, 'content', node, expectedStructure);
|
|
59
145
|
}
|
|
60
146
|
|
|
61
|
-
yield* this.verifyFileExtendedAttributes(node,
|
|
147
|
+
yield* this.verifyFileExtendedAttributes(node, expectedStructure);
|
|
62
148
|
|
|
63
|
-
if (options
|
|
149
|
+
if (this.options.verifyContent === 'peakOnly') {
|
|
150
|
+
yield* this.verifyContentPeak(node);
|
|
151
|
+
} else if (this.options.verifyContent) {
|
|
64
152
|
yield* this.verifyContent(node);
|
|
65
153
|
}
|
|
66
|
-
if (options
|
|
154
|
+
if (this.options.verifyThumbnails) {
|
|
67
155
|
yield* this.verifyThumbnails(node);
|
|
68
156
|
}
|
|
157
|
+
|
|
158
|
+
if (expectedStructure?.expectedMediaType) {
|
|
159
|
+
const mediaType = getMediaType(node);
|
|
160
|
+
if (mediaType !== expectedStructure.expectedMediaType) {
|
|
161
|
+
yield {
|
|
162
|
+
type: 'expected_structure_integrity_error',
|
|
163
|
+
expectedNode: getExpectedTreeNodeDetails(expectedStructure),
|
|
164
|
+
...getNodeDetails(node),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
69
168
|
}
|
|
70
169
|
|
|
71
|
-
private async *verifyAuthor(
|
|
170
|
+
private async *verifyAuthor(
|
|
171
|
+
author: Author,
|
|
172
|
+
authorType: 'key' | 'name' | 'content',
|
|
173
|
+
node: MaybeNode,
|
|
174
|
+
expectedStructure?: ExpectedTreeNode,
|
|
175
|
+
): AsyncGenerator<DiagnosticResult> {
|
|
72
176
|
if (!author.ok) {
|
|
73
177
|
yield {
|
|
74
178
|
type: 'unverified_author',
|
|
@@ -78,11 +182,31 @@ export class SDKDiagnostic {
|
|
|
78
182
|
...getNodeDetails(node),
|
|
79
183
|
};
|
|
80
184
|
}
|
|
185
|
+
|
|
186
|
+
if (expectedStructure?.expectedAuthors) {
|
|
187
|
+
let expectedEmail: string | null | undefined =
|
|
188
|
+
typeof expectedStructure.expectedAuthors === 'string'
|
|
189
|
+
? expectedStructure.expectedAuthors
|
|
190
|
+
: expectedStructure.expectedAuthors[authorType];
|
|
191
|
+
|
|
192
|
+
if (expectedEmail === 'anonymous') {
|
|
193
|
+
expectedEmail = null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const email = author.ok ? author.value : author.error.claimedAuthor;
|
|
197
|
+
if (expectedEmail !== undefined && email !== expectedEmail) {
|
|
198
|
+
yield {
|
|
199
|
+
type: 'expected_structure_integrity_error',
|
|
200
|
+
expectedNode: getExpectedTreeNodeDetails(expectedStructure),
|
|
201
|
+
...getNodeDetails(node),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
81
205
|
}
|
|
82
206
|
|
|
83
207
|
private async *verifyFileExtendedAttributes(
|
|
84
208
|
node: MaybeNode,
|
|
85
|
-
|
|
209
|
+
expectedStructure?: ExpectedTreeNode,
|
|
86
210
|
): AsyncGenerator<DiagnosticResult> {
|
|
87
211
|
const activeRevision = getActiveRevision(node);
|
|
88
212
|
|
|
@@ -108,9 +232,9 @@ export class SDKDiagnostic {
|
|
|
108
232
|
};
|
|
109
233
|
}
|
|
110
234
|
|
|
111
|
-
if (
|
|
112
|
-
const expectedSha1 =
|
|
113
|
-
const expectedSizeInBytes =
|
|
235
|
+
if (expectedStructure) {
|
|
236
|
+
const expectedSha1 = expectedStructure.expectedSha1;
|
|
237
|
+
const expectedSizeInBytes = expectedStructure.expectedSizeInBytes;
|
|
114
238
|
|
|
115
239
|
const wrongSha1 = expectedSha1 !== undefined && claimedSha1 !== expectedSha1;
|
|
116
240
|
const wrongSizeInBytes = expectedSizeInBytes !== undefined && claimedSizeInBytes !== expectedSizeInBytes;
|
|
@@ -120,13 +244,49 @@ export class SDKDiagnostic {
|
|
|
120
244
|
type: 'expected_structure_integrity_error',
|
|
121
245
|
claimedSha1,
|
|
122
246
|
claimedSizeInBytes,
|
|
123
|
-
expectedNode: getExpectedTreeNodeDetails(
|
|
247
|
+
expectedNode: getExpectedTreeNodeDetails(expectedStructure),
|
|
124
248
|
...getNodeDetails(node),
|
|
125
249
|
};
|
|
126
250
|
}
|
|
127
251
|
}
|
|
128
252
|
}
|
|
129
253
|
|
|
254
|
+
private async *verifyContentPeak(node: MaybeNode): AsyncGenerator<DiagnosticResult> {
|
|
255
|
+
if (getNodeType(node) !== NodeType.File) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
let downloader: FileDownloader;
|
|
260
|
+
try {
|
|
261
|
+
downloader = await this.sdkClient.getFileDownloader(node);
|
|
262
|
+
} catch (error: unknown) {
|
|
263
|
+
yield {
|
|
264
|
+
type: 'sdk_error',
|
|
265
|
+
call: `getFileDownloader(${node.ok ? node.value.uid : node.error.uid})`,
|
|
266
|
+
error,
|
|
267
|
+
};
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
const stream = downloader.getSeekableStream();
|
|
273
|
+
const peak = await stream.read(1024);
|
|
274
|
+
if (peak.value.length === 0) {
|
|
275
|
+
yield {
|
|
276
|
+
type: 'content_download_error',
|
|
277
|
+
error: new Error('No data read'),
|
|
278
|
+
...getNodeDetails(node),
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
} catch (error: unknown) {
|
|
282
|
+
yield {
|
|
283
|
+
type: 'content_download_error',
|
|
284
|
+
error,
|
|
285
|
+
...getNodeDetails(node),
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
130
290
|
private async *verifyContent(node: MaybeNode): AsyncGenerator<DiagnosticResult> {
|
|
131
291
|
if (getNodeType(node) !== NodeType.File) {
|
|
132
292
|
return;
|
|
@@ -142,11 +302,11 @@ export class SDKDiagnostic {
|
|
|
142
302
|
|
|
143
303
|
let downloader: FileDownloader;
|
|
144
304
|
try {
|
|
145
|
-
downloader = await this.
|
|
305
|
+
downloader = await this.sdkClient.getFileDownloader(node);
|
|
146
306
|
} catch (error: unknown) {
|
|
147
307
|
yield {
|
|
148
308
|
type: 'sdk_error',
|
|
149
|
-
call: `
|
|
309
|
+
call: `getFileDownloader(${node.ok ? node.value.uid : node.error.uid})`,
|
|
150
310
|
error,
|
|
151
311
|
};
|
|
152
312
|
return;
|
|
@@ -190,9 +350,7 @@ export class SDKDiagnostic {
|
|
|
190
350
|
const nodeUid = node.ok ? node.value.uid : node.error.uid;
|
|
191
351
|
|
|
192
352
|
try {
|
|
193
|
-
const result = await Array.fromAsync(
|
|
194
|
-
this.protonDriveClient.iterateThumbnails([nodeUid], ThumbnailType.Type1),
|
|
195
|
-
);
|
|
353
|
+
const result = await Array.fromAsync(this.sdkClient.iterateThumbnails([nodeUid], ThumbnailType.Type1));
|
|
196
354
|
|
|
197
355
|
if (result.length === 0) {
|
|
198
356
|
yield {
|
|
@@ -217,162 +375,4 @@ export class SDKDiagnostic {
|
|
|
217
375
|
};
|
|
218
376
|
}
|
|
219
377
|
}
|
|
220
|
-
|
|
221
|
-
private async *verifyNodeChildren(
|
|
222
|
-
parentNode: MaybeNode,
|
|
223
|
-
options?: DiagnosticOptions,
|
|
224
|
-
): AsyncGenerator<DiagnosticResult> {
|
|
225
|
-
const parentNodeUid = parentNode.ok ? parentNode.value.uid : parentNode.error.uid;
|
|
226
|
-
const children: MaybeNode[] = [];
|
|
227
|
-
|
|
228
|
-
try {
|
|
229
|
-
for await (const child of this.protonDriveClient.iterateFolderChildren(parentNode)) {
|
|
230
|
-
if (options?.expectedStructure) {
|
|
231
|
-
children.push(child);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
yield *
|
|
235
|
-
this.verifyNodeTree(child, {
|
|
236
|
-
...options,
|
|
237
|
-
expectedStructure: options?.expectedStructure
|
|
238
|
-
? getTreeNodeChildByNodeName(options.expectedStructure, getNodeName(child))
|
|
239
|
-
: undefined,
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
} catch (error: unknown) {
|
|
243
|
-
yield {
|
|
244
|
-
type: 'sdk_error',
|
|
245
|
-
call: `iterateFolderChildren(${parentNodeUid})`,
|
|
246
|
-
error,
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (options?.expectedStructure) {
|
|
251
|
-
yield* this.verifyExpectedNodeChildren(parentNodeUid, children, options);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
private async *verifyExpectedNodeChildren(
|
|
256
|
-
parentNodeUid: string,
|
|
257
|
-
children: MaybeNode[],
|
|
258
|
-
options: DiagnosticOptions,
|
|
259
|
-
): AsyncGenerator<DiagnosticResult> {
|
|
260
|
-
if (!options.expectedStructure) {
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const expectedNodes = options.expectedStructure.children ?? [];
|
|
265
|
-
const actualNodeNames = children.map((child) => getNodeName(child));
|
|
266
|
-
|
|
267
|
-
for (const expectedNode of expectedNodes) {
|
|
268
|
-
if (!actualNodeNames.includes(expectedNode.name)) {
|
|
269
|
-
yield {
|
|
270
|
-
type: 'expected_structure_missing_node',
|
|
271
|
-
expectedNode: getExpectedTreeNodeDetails(expectedNode),
|
|
272
|
-
parentNodeUid,
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
for (const child of children) {
|
|
278
|
-
const childName = getNodeName(child);
|
|
279
|
-
const isExpected = expectedNodes.some((expectedNode) => expectedNode.name === childName);
|
|
280
|
-
|
|
281
|
-
if (!isExpected) {
|
|
282
|
-
yield {
|
|
283
|
-
type: 'expected_structure_unexpected_node',
|
|
284
|
-
...getNodeDetails(child),
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function getNodeDetails(node: MaybeNode): NodeDetails {
|
|
292
|
-
const errors: {
|
|
293
|
-
field: string;
|
|
294
|
-
error: unknown;
|
|
295
|
-
}[] = [];
|
|
296
|
-
|
|
297
|
-
if (!node.ok) {
|
|
298
|
-
const degradedNode = node.error;
|
|
299
|
-
if (!degradedNode.name.ok) {
|
|
300
|
-
errors.push({
|
|
301
|
-
field: 'name',
|
|
302
|
-
error: degradedNode.name.error,
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
if (degradedNode.activeRevision?.ok === false) {
|
|
306
|
-
errors.push({
|
|
307
|
-
field: 'activeRevision',
|
|
308
|
-
error: degradedNode.activeRevision.error,
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
for (const error of degradedNode.errors ?? []) {
|
|
312
|
-
if (error instanceof Error) {
|
|
313
|
-
errors.push({
|
|
314
|
-
field: 'error',
|
|
315
|
-
error,
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
return {
|
|
322
|
-
safeNodeDetails: {
|
|
323
|
-
...getNodeUids(node),
|
|
324
|
-
nodeType: getNodeType(node),
|
|
325
|
-
nodeCreationTime: node.ok ? node.value.creationTime : node.error.creationTime,
|
|
326
|
-
keyAuthor: node.ok ? node.value.keyAuthor : node.error.keyAuthor,
|
|
327
|
-
nameAuthor: node.ok ? node.value.nameAuthor : node.error.nameAuthor,
|
|
328
|
-
errors,
|
|
329
|
-
},
|
|
330
|
-
sensitiveNodeDetails: node,
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function getNodeUids(node: MaybeNode): { nodeUid: string; revisionUid?: string } {
|
|
335
|
-
const activeRevision = getActiveRevision(node);
|
|
336
|
-
return {
|
|
337
|
-
nodeUid: node.ok ? node.value.uid : node.error.uid,
|
|
338
|
-
revisionUid: activeRevision?.uid,
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
function getNodeType(node: MaybeNode): NodeType {
|
|
343
|
-
return node.ok ? node.value.type : node.error.type;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
function getActiveRevision(node: MaybeNode): Revision | undefined {
|
|
347
|
-
if (node.ok) {
|
|
348
|
-
return node.value.activeRevision;
|
|
349
|
-
}
|
|
350
|
-
if (node.error.activeRevision?.ok) {
|
|
351
|
-
return node.error.activeRevision.value;
|
|
352
|
-
}
|
|
353
|
-
return undefined;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
function getNodeName(node: MaybeNode): string {
|
|
357
|
-
if (node.ok) {
|
|
358
|
-
return node.value.name;
|
|
359
|
-
}
|
|
360
|
-
if (node.error.name.ok) {
|
|
361
|
-
return node.error.name.value;
|
|
362
|
-
}
|
|
363
|
-
return 'N/A';
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
function getExpectedTreeNodeDetails(expectedNode: ExcpectedTreeNode): ExcpectedTreeNode {
|
|
367
|
-
return {
|
|
368
|
-
...expectedNode,
|
|
369
|
-
children: undefined,
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
function getTreeNodeChildByNodeName(
|
|
374
|
-
expectedSubtree: ExcpectedTreeNode,
|
|
375
|
-
nodeName: string,
|
|
376
|
-
): ExcpectedTreeNode | undefined {
|
|
377
|
-
return expectedSubtree.children?.find((expectedNode) => expectedNode.name === nodeName);
|
|
378
378
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { MaybeNode, NodeType } from '../interface';
|
|
2
|
+
import { ProtonDriveClient } from '../protonDriveClient';
|
|
3
|
+
import { DiagnosticOptions, DiagnosticProgressCallback, DiagnosticResult, ExpectedTreeNode } from './interface';
|
|
4
|
+
import { zipGenerators } from './zipGenerators';
|
|
5
|
+
import { getNodeType, getNodeName, getTreeNodeChildByNodeName } from './nodeUtils';
|
|
6
|
+
import { SDKDiagnosticBase } from './sdkDiagnosticBase';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Diagnostic tool that uses the main Drive SDK to traverse and verify
|
|
10
|
+
* the integrity of the node tree.
|
|
11
|
+
*/
|
|
12
|
+
export class SDKDiagnosticMain extends SDKDiagnosticBase {
|
|
13
|
+
constructor(
|
|
14
|
+
private protonDriveClient: ProtonDriveClient,
|
|
15
|
+
options?: Pick<DiagnosticOptions, 'verifyContent' | 'verifyThumbnails'>,
|
|
16
|
+
onProgress?: DiagnosticProgressCallback,
|
|
17
|
+
) {
|
|
18
|
+
super(protonDriveClient, options, onProgress);
|
|
19
|
+
this.protonDriveClient = protonDriveClient;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async *verifyMyFiles(expectedStructure?: ExpectedTreeNode): AsyncGenerator<DiagnosticResult> {
|
|
23
|
+
let myFilesRootFolder: MaybeNode;
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
myFilesRootFolder = await this.protonDriveClient.getMyFilesRootFolder();
|
|
27
|
+
} catch (error: unknown) {
|
|
28
|
+
yield {
|
|
29
|
+
type: 'fatal_error',
|
|
30
|
+
message: `Error getting my files root folder`,
|
|
31
|
+
error,
|
|
32
|
+
};
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
yield* this.verifyNodeTree(myFilesRootFolder, expectedStructure);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async *verifyNodeTree(node: MaybeNode, expectedStructure?: ExpectedTreeNode): AsyncGenerator<DiagnosticResult> {
|
|
40
|
+
this.startProgress();
|
|
41
|
+
this.nodesQueue.push({ node, expected: expectedStructure });
|
|
42
|
+
this.loadedNodes++;
|
|
43
|
+
yield* zipGenerators(this.loadNodeTree(node, expectedStructure), this.verifyNodesQueue());
|
|
44
|
+
this.finishProgress();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private async *loadNodeTree(
|
|
48
|
+
parentNode: MaybeNode,
|
|
49
|
+
expectedStructure?: ExpectedTreeNode,
|
|
50
|
+
): AsyncGenerator<DiagnosticResult> {
|
|
51
|
+
const isFolder = getNodeType(parentNode) === NodeType.Folder;
|
|
52
|
+
if (isFolder) {
|
|
53
|
+
yield* this.loadNodeTreeRecursively(parentNode, expectedStructure);
|
|
54
|
+
}
|
|
55
|
+
this.allNodesLoaded = true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private async *loadNodeTreeRecursively(
|
|
59
|
+
parentNode: MaybeNode,
|
|
60
|
+
expectedStructure?: ExpectedTreeNode,
|
|
61
|
+
): AsyncGenerator<DiagnosticResult> {
|
|
62
|
+
const parentNodeUid = parentNode.ok ? parentNode.value.uid : parentNode.error.uid;
|
|
63
|
+
const children: MaybeNode[] = [];
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
for await (const child of this.protonDriveClient.iterateFolderChildren(parentNode)) {
|
|
67
|
+
children.push(child);
|
|
68
|
+
this.nodesQueue.push({
|
|
69
|
+
node: child,
|
|
70
|
+
expected: getTreeNodeChildByNodeName(expectedStructure, getNodeName(child)),
|
|
71
|
+
});
|
|
72
|
+
this.loadedNodes++;
|
|
73
|
+
}
|
|
74
|
+
} catch (error: unknown) {
|
|
75
|
+
yield {
|
|
76
|
+
type: 'sdk_error',
|
|
77
|
+
call: `iterateFolderChildren(${parentNodeUid})`,
|
|
78
|
+
error,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (expectedStructure) {
|
|
83
|
+
yield* this.verifyExpectedNodeChildren(parentNodeUid, children, expectedStructure);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
for (const child of children) {
|
|
87
|
+
if (getNodeType(child) === NodeType.Folder) {
|
|
88
|
+
yield* this.loadNodeTreeRecursively(
|
|
89
|
+
child,
|
|
90
|
+
getTreeNodeChildByNodeName(expectedStructure, getNodeName(child)),
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { MaybeNode } from '../interface';
|
|
2
|
+
import { ProtonDrivePhotosClient } from '../protonDrivePhotosClient';
|
|
3
|
+
import { DiagnosticOptions, DiagnosticProgressCallback, DiagnosticResult, ExpectedTreeNode } from './interface';
|
|
4
|
+
import { zipGenerators } from './zipGenerators';
|
|
5
|
+
import { getNodeName, getTreeNodeChildByNodeName } from './nodeUtils';
|
|
6
|
+
import { SDKDiagnosticBase } from './sdkDiagnosticBase';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Diagnostic tool that uses the Photos SDK to traverse and verify
|
|
10
|
+
* the integrity of the Photos in the timeline.
|
|
11
|
+
*/
|
|
12
|
+
export class SDKDiagnosticPhotos extends SDKDiagnosticBase {
|
|
13
|
+
constructor(
|
|
14
|
+
private protonDrivePhotosClient: ProtonDrivePhotosClient,
|
|
15
|
+
options?: Pick<DiagnosticOptions, 'verifyContent' | 'verifyThumbnails'>,
|
|
16
|
+
onProgress?: DiagnosticProgressCallback,
|
|
17
|
+
) {
|
|
18
|
+
super(protonDrivePhotosClient, options, onProgress);
|
|
19
|
+
this.protonDrivePhotosClient = protonDrivePhotosClient;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async *verifyTimeline(expectedStructure?: ExpectedTreeNode): AsyncGenerator<DiagnosticResult> {
|
|
23
|
+
this.startProgress();
|
|
24
|
+
yield* zipGenerators(this.loadTimeline(expectedStructure), this.verifyNodesQueue());
|
|
25
|
+
this.finishProgress();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private async *loadTimeline(expectedStructure?: ExpectedTreeNode): AsyncGenerator<DiagnosticResult> {
|
|
29
|
+
let nodeUids: string[] = [];
|
|
30
|
+
try {
|
|
31
|
+
const results = await Array.fromAsync(this.protonDrivePhotosClient.iterateTimeline());
|
|
32
|
+
nodeUids = results.map((result) => result.nodeUid);
|
|
33
|
+
this.loadedNodes = nodeUids.length;
|
|
34
|
+
} catch (error: unknown) {
|
|
35
|
+
yield {
|
|
36
|
+
type: 'sdk_error',
|
|
37
|
+
call: `iterateTimeline()`,
|
|
38
|
+
error,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const photos: MaybeNode[] = [];
|
|
43
|
+
try {
|
|
44
|
+
for await (const maybeMissingNode of this.protonDrivePhotosClient.iterateNodes(nodeUids)) {
|
|
45
|
+
if (!maybeMissingNode.ok && 'missingUid' in maybeMissingNode.error) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const maybeNode = maybeMissingNode as MaybeNode;
|
|
49
|
+
|
|
50
|
+
photos.push(maybeNode);
|
|
51
|
+
this.nodesQueue.push({
|
|
52
|
+
node: maybeNode,
|
|
53
|
+
expected: getTreeNodeChildByNodeName(expectedStructure, getNodeName(maybeNode)),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
} catch (error: unknown) {
|
|
57
|
+
yield {
|
|
58
|
+
type: 'sdk_error',
|
|
59
|
+
call: `iterateNodes(...)`,
|
|
60
|
+
error,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (expectedStructure) {
|
|
65
|
+
yield* this.verifyExpectedNodeChildren('photo-timeline', photos, expectedStructure);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
this.allNodesLoaded = true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -13,7 +13,7 @@ export type DeviceMetadata = {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
export interface SharesService {
|
|
16
|
-
|
|
16
|
+
getRootIDs(): Promise<{ volumeId: string }>;
|
|
17
17
|
getMyFilesShareMemberEmailKey(): Promise<{
|
|
18
18
|
addressId: string;
|
|
19
19
|
email: string;
|