@protontech/drive-sdk 0.5.1 → 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.
Files changed (201) hide show
  1. package/dist/diagnostic/diagnostic.d.ts +7 -4
  2. package/dist/diagnostic/diagnostic.js +16 -8
  3. package/dist/diagnostic/diagnostic.js.map +1 -1
  4. package/dist/diagnostic/index.d.ts +1 -1
  5. package/dist/diagnostic/index.js +9 -1
  6. package/dist/diagnostic/index.js.map +1 -1
  7. package/dist/diagnostic/interface.d.ts +24 -9
  8. package/dist/diagnostic/nodeUtils.d.ts +13 -0
  9. package/dist/diagnostic/nodeUtils.js +90 -0
  10. package/dist/diagnostic/nodeUtils.js.map +1 -0
  11. package/dist/diagnostic/sdkDiagnosticBase.d.ts +36 -0
  12. package/dist/diagnostic/sdkDiagnosticBase.js +305 -0
  13. package/dist/diagnostic/sdkDiagnosticBase.js.map +1 -0
  14. package/dist/diagnostic/sdkDiagnosticMain.d.ts +16 -0
  15. package/dist/diagnostic/sdkDiagnosticMain.js +79 -0
  16. package/dist/diagnostic/sdkDiagnosticMain.js.map +1 -0
  17. package/dist/diagnostic/sdkDiagnosticPhotos.d.ts +13 -0
  18. package/dist/diagnostic/sdkDiagnosticPhotos.js +65 -0
  19. package/dist/diagnostic/sdkDiagnosticPhotos.js.map +1 -0
  20. package/dist/interface/index.d.ts +1 -1
  21. package/dist/interface/upload.d.ts +1 -12
  22. package/dist/internal/devices/interface.d.ts +1 -1
  23. package/dist/internal/devices/manager.js +1 -1
  24. package/dist/internal/devices/manager.js.map +1 -1
  25. package/dist/internal/devices/manager.test.js +3 -3
  26. package/dist/internal/devices/manager.test.js.map +1 -1
  27. package/dist/internal/errors.d.ts +5 -0
  28. package/dist/internal/errors.js +23 -0
  29. package/dist/internal/errors.js.map +1 -1
  30. package/dist/internal/errors.test.js +53 -2
  31. package/dist/internal/errors.test.js.map +1 -1
  32. package/dist/internal/nodes/apiService.d.ts +11 -1
  33. package/dist/internal/nodes/apiService.js +20 -1
  34. package/dist/internal/nodes/apiService.js.map +1 -1
  35. package/dist/internal/nodes/apiService.test.js +1 -1
  36. package/dist/internal/nodes/apiService.test.js.map +1 -1
  37. package/dist/internal/nodes/cryptoReporter.js +3 -0
  38. package/dist/internal/nodes/cryptoReporter.js.map +1 -1
  39. package/dist/internal/nodes/cryptoService.d.ts +4 -0
  40. package/dist/internal/nodes/cryptoService.js +6 -0
  41. package/dist/internal/nodes/cryptoService.js.map +1 -1
  42. package/dist/internal/nodes/index.d.ts +1 -1
  43. package/dist/internal/nodes/index.js +2 -2
  44. package/dist/internal/nodes/index.js.map +1 -1
  45. package/dist/internal/nodes/index.test.js +2 -2
  46. package/dist/internal/nodes/index.test.js.map +1 -1
  47. package/dist/internal/nodes/interface.d.ts +1 -1
  48. package/dist/internal/nodes/nodeName.d.ts +8 -0
  49. package/dist/internal/nodes/nodeName.js +30 -0
  50. package/dist/internal/nodes/nodeName.js.map +1 -0
  51. package/dist/internal/nodes/nodeName.test.d.ts +1 -0
  52. package/dist/internal/nodes/nodeName.test.js +50 -0
  53. package/dist/internal/nodes/nodeName.test.js.map +1 -0
  54. package/dist/internal/nodes/nodesAccess.d.ts +1 -1
  55. package/dist/internal/nodes/nodesAccess.js +4 -4
  56. package/dist/internal/nodes/nodesAccess.js.map +1 -1
  57. package/dist/internal/nodes/nodesAccess.test.js +2 -2
  58. package/dist/internal/nodes/nodesAccess.test.js.map +1 -1
  59. package/dist/internal/nodes/nodesManagement.d.ts +1 -0
  60. package/dist/internal/nodes/nodesManagement.js +30 -1
  61. package/dist/internal/nodes/nodesManagement.js.map +1 -1
  62. package/dist/internal/nodes/nodesManagement.test.js +61 -0
  63. package/dist/internal/nodes/nodesManagement.test.js.map +1 -1
  64. package/dist/internal/photos/albums.js +1 -1
  65. package/dist/internal/photos/albums.js.map +1 -1
  66. package/dist/internal/photos/apiService.d.ts +6 -0
  67. package/dist/internal/photos/apiService.js +16 -0
  68. package/dist/internal/photos/apiService.js.map +1 -1
  69. package/dist/internal/photos/index.d.ts +3 -1
  70. package/dist/internal/photos/index.js +6 -2
  71. package/dist/internal/photos/index.js.map +1 -1
  72. package/dist/internal/photos/interface.d.ts +4 -1
  73. package/dist/internal/photos/shares.d.ts +1 -1
  74. package/dist/internal/photos/shares.js +3 -3
  75. package/dist/internal/photos/shares.js.map +1 -1
  76. package/dist/internal/photos/timeline.d.ts +8 -1
  77. package/dist/internal/photos/timeline.js +36 -2
  78. package/dist/internal/photos/timeline.js.map +1 -1
  79. package/dist/internal/photos/timeline.test.d.ts +1 -0
  80. package/dist/internal/photos/timeline.test.js +99 -0
  81. package/dist/internal/photos/timeline.test.js.map +1 -0
  82. package/dist/internal/shares/cryptoService.js +3 -0
  83. package/dist/internal/shares/cryptoService.js.map +1 -1
  84. package/dist/internal/shares/index.d.ts +1 -0
  85. package/dist/internal/shares/index.js +3 -0
  86. package/dist/internal/shares/index.js.map +1 -1
  87. package/dist/internal/shares/interface.d.ts +8 -0
  88. package/dist/internal/shares/interface.js +10 -1
  89. package/dist/internal/shares/interface.js.map +1 -1
  90. package/dist/internal/shares/manager.d.ts +1 -1
  91. package/dist/internal/shares/manager.js +4 -4
  92. package/dist/internal/shares/manager.js.map +1 -1
  93. package/dist/internal/shares/manager.test.js +7 -7
  94. package/dist/internal/shares/manager.test.js.map +1 -1
  95. package/dist/internal/sharing/apiService.d.ts +3 -1
  96. package/dist/internal/sharing/apiService.js +16 -12
  97. package/dist/internal/sharing/apiService.js.map +1 -1
  98. package/dist/internal/sharing/index.d.ts +2 -1
  99. package/dist/internal/sharing/index.js +6 -2
  100. package/dist/internal/sharing/index.js.map +1 -1
  101. package/dist/internal/sharing/interface.d.ts +1 -1
  102. package/dist/internal/sharing/sharingAccess.js +1 -1
  103. package/dist/internal/sharing/sharingAccess.js.map +1 -1
  104. package/dist/internal/sharing/sharingAccess.test.js +1 -1
  105. package/dist/internal/sharing/sharingAccess.test.js.map +1 -1
  106. package/dist/internal/sharing/sharingManagement.js +32 -14
  107. package/dist/internal/sharing/sharingManagement.js.map +1 -1
  108. package/dist/internal/sharing/sharingManagement.test.js +46 -1
  109. package/dist/internal/sharing/sharingManagement.test.js.map +1 -1
  110. package/dist/internal/sharingPublic/cryptoReporter.js +3 -0
  111. package/dist/internal/sharingPublic/cryptoReporter.js.map +1 -1
  112. package/dist/internal/sharingPublic/index.d.ts +3 -0
  113. package/dist/internal/sharingPublic/index.js +5 -1
  114. package/dist/internal/sharingPublic/index.js.map +1 -1
  115. package/dist/internal/sharingPublic/shares.d.ts +1 -1
  116. package/dist/internal/sharingPublic/shares.js +1 -2
  117. package/dist/internal/sharingPublic/shares.js.map +1 -1
  118. package/dist/internal/upload/apiService.d.ts +0 -9
  119. package/dist/internal/upload/apiService.js +0 -16
  120. package/dist/internal/upload/apiService.js.map +1 -1
  121. package/dist/internal/upload/cryptoService.d.ts +0 -4
  122. package/dist/internal/upload/cryptoService.js +0 -6
  123. package/dist/internal/upload/cryptoService.js.map +1 -1
  124. package/dist/internal/upload/fileUploader.d.ts +0 -1
  125. package/dist/internal/upload/fileUploader.js +0 -4
  126. package/dist/internal/upload/fileUploader.js.map +1 -1
  127. package/dist/internal/upload/manager.d.ts +0 -1
  128. package/dist/internal/upload/manager.js +0 -51
  129. package/dist/internal/upload/manager.js.map +1 -1
  130. package/dist/internal/upload/manager.test.js +0 -61
  131. package/dist/internal/upload/manager.test.js.map +1 -1
  132. package/dist/protonDriveClient.d.ts +17 -2
  133. package/dist/protonDriveClient.js +19 -1
  134. package/dist/protonDriveClient.js.map +1 -1
  135. package/dist/protonDrivePhotosClient.d.ts +119 -4
  136. package/dist/protonDrivePhotosClient.js +183 -10
  137. package/dist/protonDrivePhotosClient.js.map +1 -1
  138. package/dist/protonDrivePublicLinkClient.d.ts +33 -1
  139. package/dist/protonDrivePublicLinkClient.js +51 -2
  140. package/dist/protonDrivePublicLinkClient.js.map +1 -1
  141. package/package.json +1 -1
  142. package/src/diagnostic/diagnostic.ts +27 -8
  143. package/src/diagnostic/index.ts +17 -2
  144. package/src/diagnostic/interface.ts +35 -9
  145. package/src/diagnostic/nodeUtils.ts +100 -0
  146. package/src/diagnostic/{sdkDiagnostic.ts → sdkDiagnosticBase.ts} +204 -204
  147. package/src/diagnostic/sdkDiagnosticMain.ts +95 -0
  148. package/src/diagnostic/sdkDiagnosticPhotos.ts +70 -0
  149. package/src/interface/index.ts +1 -1
  150. package/src/interface/upload.ts +1 -13
  151. package/src/internal/devices/interface.ts +1 -1
  152. package/src/internal/devices/manager.test.ts +3 -3
  153. package/src/internal/devices/manager.ts +1 -1
  154. package/src/internal/errors.test.ts +62 -1
  155. package/src/internal/errors.ts +27 -0
  156. package/src/internal/nodes/apiService.test.ts +1 -1
  157. package/src/internal/nodes/apiService.ts +42 -0
  158. package/src/internal/nodes/cryptoReporter.ts +6 -5
  159. package/src/internal/nodes/cryptoService.ts +9 -0
  160. package/src/internal/nodes/index.test.ts +2 -1
  161. package/src/internal/nodes/index.ts +2 -1
  162. package/src/internal/nodes/interface.ts +1 -1
  163. package/src/internal/nodes/nodeName.test.ts +57 -0
  164. package/src/internal/nodes/nodeName.ts +26 -0
  165. package/src/internal/nodes/nodesAccess.test.ts +2 -2
  166. package/src/internal/nodes/nodesAccess.ts +5 -5
  167. package/src/internal/nodes/nodesManagement.test.ts +65 -0
  168. package/src/internal/nodes/nodesManagement.ts +43 -1
  169. package/src/internal/photos/albums.ts +1 -1
  170. package/src/internal/photos/apiService.ts +40 -0
  171. package/src/internal/photos/index.ts +13 -1
  172. package/src/internal/photos/interface.ts +4 -1
  173. package/src/internal/photos/shares.ts +3 -3
  174. package/src/internal/photos/timeline.test.ts +116 -0
  175. package/src/internal/photos/timeline.ts +47 -2
  176. package/src/internal/shares/cryptoService.ts +5 -1
  177. package/src/internal/shares/index.ts +1 -0
  178. package/src/internal/shares/interface.ts +9 -0
  179. package/src/internal/shares/manager.test.ts +7 -7
  180. package/src/internal/shares/manager.ts +4 -4
  181. package/src/internal/sharing/apiService.ts +15 -12
  182. package/src/internal/sharing/index.ts +7 -1
  183. package/src/internal/sharing/interface.ts +1 -1
  184. package/src/internal/sharing/sharingAccess.test.ts +1 -1
  185. package/src/internal/sharing/sharingAccess.ts +1 -1
  186. package/src/internal/sharing/sharingManagement.test.ts +59 -1
  187. package/src/internal/sharing/sharingManagement.ts +33 -14
  188. package/src/internal/sharingPublic/cryptoReporter.ts +5 -1
  189. package/src/internal/sharingPublic/index.ts +5 -1
  190. package/src/internal/sharingPublic/shares.ts +1 -2
  191. package/src/internal/upload/apiService.ts +0 -39
  192. package/src/internal/upload/cryptoService.ts +0 -9
  193. package/src/internal/upload/fileUploader.ts +0 -5
  194. package/src/internal/upload/manager.test.ts +0 -65
  195. package/src/internal/upload/manager.ts +0 -64
  196. package/src/protonDriveClient.ts +21 -2
  197. package/src/protonDrivePhotosClient.ts +217 -9
  198. package/src/protonDrivePublicLinkClient.ts +77 -2
  199. package/dist/diagnostic/sdkDiagnostic.d.ts +0 -23
  200. package/dist/diagnostic/sdkDiagnostic.js +0 -320
  201. package/dist/diagnostic/sdkDiagnostic.js.map +0 -1
@@ -1,48 +1,129 @@
1
- import { Author, FileDownloader, MaybeNode, NodeType, Revision, ThumbnailType } from '../interface';
2
- import { ProtonDriveClient } from '../protonDriveClient';
3
- import { DiagnosticOptions, DiagnosticResult, NodeDetails, ExcpectedTreeNode } from './interface';
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
- * Diagnostic tool that uses SDK to traverse the node tree and verify
8
- * the integrity of the node tree.
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 SDKDiagnostic {
14
- constructor(private protonDriveClient: ProtonDriveClient) {
15
- this.protonDriveClient = protonDriveClient;
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
- async *verifyMyFiles(options?: DiagnosticOptions): AsyncGenerator<DiagnosticResult> {
19
- let myFilesRootFolder: MaybeNode;
50
+ protected startProgress(): void {
51
+ this.allNodesLoaded = false;
52
+ this.loadedNodes = 0;
53
+ this.checkedNodes = 0;
20
54
 
21
- try {
22
- myFilesRootFolder = await this.protonDriveClient.getMyFilesRootFolder();
23
- } catch (error: unknown) {
24
- yield {
25
- type: 'fatal_error',
26
- message: `Error getting my files root folder`,
27
- error,
28
- };
29
- return;
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
- yield* this.verifyNodeTree(myFilesRootFolder, options);
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 *verifyNodeTree(node: MaybeNode, options?: DiagnosticOptions): AsyncGenerator<DiagnosticResult> {
36
- const isFolder = getNodeType(node) === NodeType.Folder;
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
- yield* this.verifyNode(node, options);
100
+ for (const child of children) {
101
+ const childName = getNodeName(child);
102
+ const isExpected = expectedNodes.some((expectedNode) => expectedNode.name === childName);
39
103
 
40
- if (isFolder) {
41
- yield* this.verifyNodeChildren(node, options);
104
+ if (!isExpected) {
105
+ yield {
106
+ type: 'expected_structure_unexpected_node',
107
+ ...getNodeDetails(child),
108
+ };
109
+ }
42
110
  }
43
111
  }
44
112
 
45
- private async *verifyNode(node: MaybeNode, options?: DiagnosticOptions): AsyncGenerator<DiagnosticResult> {
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(node.ok ? node.value.nameAuthor : node.error.nameAuthor, 'name', node);
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, options);
147
+ yield* this.verifyFileExtendedAttributes(node, expectedStructure);
62
148
 
63
- if (options?.verifyContent) {
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?.verifyThumbnails) {
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(author: Author, authorType: string, node: MaybeNode): AsyncGenerator<DiagnosticResult> {
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
- options?: DiagnosticOptions,
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 (options?.expectedStructure) {
112
- const expectedSha1 = options.expectedStructure.expectedSha1;
113
- const expectedSizeInBytes = options.expectedStructure.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(options.expectedStructure),
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.protonDriveClient.getFileRevisionDownloader(activeRevision.uid);
305
+ downloader = await this.sdkClient.getFileDownloader(node);
146
306
  } catch (error: unknown) {
147
307
  yield {
148
308
  type: 'sdk_error',
149
- call: `getFileRevisionDownloader(${activeRevision.uid})`,
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
+ }
@@ -82,7 +82,7 @@ export type {
82
82
  MetricEvent,
83
83
  } from './telemetry';
84
84
  export { MetricVolumeType } from './telemetry';
85
- export type { FileUploader, FileRevisionUploader, UploadController, UploadMetadata } from './upload';
85
+ export type { FileUploader, UploadController, UploadMetadata } from './upload';
86
86
  export type { Thumbnail, ThumbnailResult } from './thumbnail';
87
87
  export { ThumbnailType } from './thumbnail';
88
88