@protontech/drive-sdk 0.12.1 → 0.13.0
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/crypto/driveCrypto.d.ts +1 -0
- package/dist/crypto/driveCrypto.js +1 -0
- package/dist/crypto/driveCrypto.js.map +1 -1
- package/dist/interface/featureFlags.d.ts +2 -1
- package/dist/interface/featureFlags.js +1 -0
- package/dist/interface/featureFlags.js.map +1 -1
- package/dist/interface/httpClient.d.ts +1 -0
- package/dist/interface/nodes.d.ts +7 -0
- package/dist/interface/nodes.js.map +1 -1
- package/dist/internal/apiService/apiService.d.ts +1 -0
- package/dist/internal/apiService/apiService.js +16 -7
- package/dist/internal/apiService/apiService.js.map +1 -1
- package/dist/internal/apiService/apiService.test.js +24 -0
- package/dist/internal/apiService/apiService.test.js.map +1 -1
- package/dist/internal/apiService/driveTypes.d.ts +110 -101
- package/dist/internal/nodes/apiService.d.ts +4 -0
- package/dist/internal/nodes/apiService.js +4 -0
- package/dist/internal/nodes/apiService.js.map +1 -1
- package/dist/internal/nodes/apiService.test.js +8 -0
- package/dist/internal/nodes/apiService.test.js.map +1 -1
- package/dist/internal/nodes/cryptoService.js +3 -0
- package/dist/internal/nodes/cryptoService.js.map +1 -1
- package/dist/internal/nodes/extendedAttributes.d.ts +5 -5
- package/dist/internal/nodes/extendedAttributes.js +5 -14
- package/dist/internal/nodes/extendedAttributes.js.map +1 -1
- package/dist/internal/nodes/extendedAttributes.test.js +16 -22
- package/dist/internal/nodes/extendedAttributes.test.js.map +1 -1
- package/dist/internal/nodes/interface.d.ts +5 -0
- package/dist/internal/nodes/nodesManagement.d.ts +3 -3
- package/dist/internal/nodes/nodesManagement.js +7 -5
- package/dist/internal/nodes/nodesManagement.js.map +1 -1
- package/dist/internal/photos/albumsManager.js +1 -0
- package/dist/internal/photos/albumsManager.js.map +1 -1
- package/dist/internal/photos/nodes.d.ts +1 -1
- package/dist/internal/photos/nodes.js +2 -2
- package/dist/internal/photos/nodes.js.map +1 -1
- package/dist/internal/photos/upload.d.ts +5 -5
- package/dist/internal/photos/upload.js +8 -2
- package/dist/internal/photos/upload.js.map +1 -1
- package/dist/internal/sharingPublic/nodes.d.ts +1 -0
- package/dist/internal/upload/apiService.d.ts +45 -1
- package/dist/internal/upload/apiService.js +69 -1
- package/dist/internal/upload/apiService.js.map +1 -1
- package/dist/internal/upload/blockVerifier.d.ts +4 -1
- package/dist/internal/upload/blockVerifier.js +5 -0
- package/dist/internal/upload/blockVerifier.js.map +1 -1
- package/dist/internal/upload/cryptoService.d.ts +2 -2
- package/dist/internal/upload/cryptoService.js +1 -3
- package/dist/internal/upload/cryptoService.js.map +1 -1
- package/dist/internal/upload/fileUploader.d.ts +4 -3
- package/dist/internal/upload/fileUploader.js +17 -7
- package/dist/internal/upload/fileUploader.js.map +1 -1
- package/dist/internal/upload/index.d.ts +3 -3
- package/dist/internal/upload/index.js +17 -1
- package/dist/internal/upload/index.js.map +1 -1
- package/dist/internal/upload/index.test.d.ts +1 -0
- package/dist/internal/upload/index.test.js +71 -0
- package/dist/internal/upload/index.test.js.map +1 -0
- package/dist/internal/upload/interface.d.ts +2 -0
- package/dist/internal/upload/manager.d.ts +41 -2
- package/dist/internal/upload/manager.js +123 -42
- package/dist/internal/upload/manager.js.map +1 -1
- package/dist/internal/upload/manager.test.js +267 -0
- package/dist/internal/upload/manager.test.js.map +1 -1
- package/dist/internal/upload/smallFileUploader.d.ts +83 -0
- package/dist/internal/upload/smallFileUploader.js +197 -0
- package/dist/internal/upload/smallFileUploader.js.map +1 -0
- package/dist/internal/upload/smallFileUploader.test.d.ts +1 -0
- package/dist/internal/upload/smallFileUploader.test.js +358 -0
- package/dist/internal/upload/smallFileUploader.test.js.map +1 -0
- package/dist/internal/upload/streamReader.d.ts +4 -0
- package/dist/internal/upload/streamReader.js +37 -0
- package/dist/internal/upload/streamReader.js.map +1 -0
- package/dist/internal/upload/streamReader.test.d.ts +1 -0
- package/dist/internal/upload/streamReader.test.js +90 -0
- package/dist/internal/upload/streamReader.test.js.map +1 -0
- package/dist/internal/upload/streamUploader.d.ts +6 -0
- package/dist/internal/upload/streamUploader.js +3 -3
- package/dist/internal/upload/streamUploader.js.map +1 -1
- package/dist/internal/upload/telemetry.d.ts +3 -2
- package/dist/internal/upload/telemetry.js +3 -0
- package/dist/internal/upload/telemetry.js.map +1 -1
- package/dist/protonDrivePhotosClient.d.ts +1 -1
- package/dist/protonDrivePublicLinkClient.js +3 -1
- package/dist/protonDrivePublicLinkClient.js.map +1 -1
- package/dist/transformers.d.ts +1 -1
- package/dist/transformers.js +1 -0
- package/dist/transformers.js.map +1 -1
- package/package.json +1 -1
- package/src/crypto/driveCrypto.ts +2 -0
- package/src/interface/featureFlags.ts +1 -0
- package/src/interface/httpClient.ts +1 -0
- package/src/interface/nodes.ts +7 -0
- package/src/internal/apiService/apiService.test.ts +30 -0
- package/src/internal/apiService/apiService.ts +23 -7
- package/src/internal/apiService/driveTypes.ts +110 -101
- package/src/internal/nodes/apiService.test.ts +9 -0
- package/src/internal/nodes/apiService.ts +4 -0
- package/src/internal/nodes/cryptoService.ts +11 -1
- package/src/internal/nodes/extendedAttributes.test.ts +25 -25
- package/src/internal/nodes/extendedAttributes.ts +10 -19
- package/src/internal/nodes/interface.ts +5 -0
- package/src/internal/nodes/nodesManagement.ts +8 -6
- package/src/internal/photos/albumsManager.ts +1 -0
- package/src/internal/photos/nodes.ts +2 -2
- package/src/internal/photos/upload.ts +23 -10
- package/src/internal/upload/apiService.ts +167 -2
- package/src/internal/upload/blockVerifier.ts +12 -0
- package/src/internal/upload/cryptoService.ts +10 -10
- package/src/internal/upload/fileUploader.ts +20 -7
- package/src/internal/upload/index.test.ts +99 -0
- package/src/internal/upload/index.ts +45 -4
- package/src/internal/upload/interface.ts +2 -0
- package/src/internal/upload/manager.test.ts +367 -1
- package/src/internal/upload/manager.ts +226 -76
- package/src/internal/upload/smallFileUploader.test.ts +491 -0
- package/src/internal/upload/smallFileUploader.ts +353 -0
- package/src/internal/upload/streamReader.test.ts +109 -0
- package/src/internal/upload/streamReader.ts +38 -0
- package/src/internal/upload/streamUploader.ts +1 -1
- package/src/internal/upload/telemetry.ts +5 -1
- package/src/protonDrivePhotosClient.ts +1 -1
- package/src/protonDrivePublicLinkClient.ts +2 -0
- package/src/transformers.ts +2 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { c } from 'ttag';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { PrivateKey, SessionKey } from '../../crypto';
|
|
4
|
+
import { Logger, ProtonDriveTelemetry, ThumbnailType, UploadMetadata } from '../../interface';
|
|
4
5
|
import { ValidationError, NodeWithSameNameExistsValidationError } from '../../errors';
|
|
5
6
|
import { ErrorCode } from '../apiService';
|
|
6
7
|
import { generateFileExtendedAttributes } from '../nodes';
|
|
@@ -32,20 +33,11 @@ export class UploadManager {
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
async createDraftNode(parentFolderUid: string, name: string, metadata: UploadMetadata): Promise<NodeRevisionDraft> {
|
|
35
|
-
const
|
|
36
|
-
if (!parentKeys.hashKey) {
|
|
37
|
-
throw new ValidationError(c('Error').t`Creating files in non-folders is not allowed`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const generatedNodeCrypto = await this.cryptoService.generateFileCrypto(
|
|
41
|
-
parentFolderUid,
|
|
42
|
-
{ key: parentKeys.key, hashKey: parentKeys.hashKey },
|
|
43
|
-
name,
|
|
44
|
-
);
|
|
36
|
+
const { parentHashKey, ...generatedNodeCrypto } = await this.generateNewFileCrypto(parentFolderUid, name);
|
|
45
37
|
|
|
46
38
|
const { nodeUid, nodeRevisionUid } = await this.createDraftOnAPI(
|
|
47
39
|
parentFolderUid,
|
|
48
|
-
|
|
40
|
+
parentHashKey,
|
|
49
41
|
name,
|
|
50
42
|
metadata,
|
|
51
43
|
generatedNodeCrypto,
|
|
@@ -60,7 +52,7 @@ export class UploadManager {
|
|
|
60
52
|
signingKeys: generatedNodeCrypto.signingKeys,
|
|
61
53
|
},
|
|
62
54
|
parentNodeKeys: {
|
|
63
|
-
hashKey:
|
|
55
|
+
hashKey: parentHashKey,
|
|
64
56
|
},
|
|
65
57
|
newNodeInfo: {
|
|
66
58
|
parentUid: parentFolderUid,
|
|
@@ -71,6 +63,57 @@ export class UploadManager {
|
|
|
71
63
|
};
|
|
72
64
|
}
|
|
73
65
|
|
|
66
|
+
async generateNewFileCrypto(
|
|
67
|
+
parentFolderUid: string,
|
|
68
|
+
name: string,
|
|
69
|
+
): Promise<NodeCrypto & { parentHashKey: Uint8Array<ArrayBuffer> }> {
|
|
70
|
+
const parentKeys = await this.nodesService.getNodeKeys(parentFolderUid);
|
|
71
|
+
if (!parentKeys.hashKey) {
|
|
72
|
+
throw new ValidationError(c('Error').t`Creating files in non-folders is not allowed`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const generatedNodeCrypto = await this.cryptoService.generateFileCrypto(
|
|
76
|
+
parentFolderUid,
|
|
77
|
+
{ key: parentKeys.key, hashKey: parentKeys.hashKey },
|
|
78
|
+
name,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
...generatedNodeCrypto,
|
|
83
|
+
parentHashKey: parentKeys.hashKey,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async getExistingFileNodeCrypto(nodeUid: string): Promise<{
|
|
88
|
+
key: PrivateKey;
|
|
89
|
+
contentKeyPacket: Uint8Array<ArrayBuffer>;
|
|
90
|
+
contentKeyPacketSessionKey: SessionKey;
|
|
91
|
+
signingKeys: NodeCrypto['signingKeys'];
|
|
92
|
+
}> {
|
|
93
|
+
const node = await this.nodesService.getNode(nodeUid);
|
|
94
|
+
const nodeKeys = await this.nodesService.getNodeKeys(nodeUid);
|
|
95
|
+
|
|
96
|
+
if (!node.activeRevision?.ok || !nodeKeys.contentKeyPacketSessionKey) {
|
|
97
|
+
throw new ValidationError(c('Error').t`Creating revisions in non-files is not allowed`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!nodeKeys.contentKeyPacket) {
|
|
101
|
+
throw new ValidationError(c('Error').t`Content key packet is required for small revision upload`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const signingKeys = await this.cryptoService.getSigningKeysForExistingNode({
|
|
105
|
+
nodeUid,
|
|
106
|
+
parentNodeUid: node.parentUid,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
key: nodeKeys.key,
|
|
111
|
+
contentKeyPacket: nodeKeys.contentKeyPacket,
|
|
112
|
+
contentKeyPacketSessionKey: nodeKeys.contentKeyPacketSessionKey,
|
|
113
|
+
signingKeys,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
74
117
|
private async createDraftOnAPI(
|
|
75
118
|
parentFolderUid: string,
|
|
76
119
|
parentHashKey: Uint8Array<ArrayBuffer>,
|
|
@@ -97,80 +140,187 @@ export class UploadManager {
|
|
|
97
140
|
});
|
|
98
141
|
return result;
|
|
99
142
|
} catch (error: unknown) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
143
|
+
return this.handleConflictError(parentFolderUid, metadata, error, async () => {
|
|
144
|
+
return this.createDraftOnAPI(parentFolderUid, parentHashKey, name, metadata, generatedNodeCrypto);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async uploadFile(
|
|
150
|
+
parentFolderUid: string,
|
|
151
|
+
nodeCrypto: NodeCrypto,
|
|
152
|
+
metadata: UploadMetadata,
|
|
153
|
+
commitPayload: {
|
|
154
|
+
armoredManifestSignature: string;
|
|
155
|
+
armoredExtendedAttributes: string;
|
|
156
|
+
},
|
|
157
|
+
encryptedBlock:
|
|
158
|
+
| {
|
|
159
|
+
encryptedData: Uint8Array<ArrayBuffer>;
|
|
160
|
+
armoredSignature: string;
|
|
161
|
+
verificationToken: Uint8Array<ArrayBuffer>;
|
|
162
|
+
}
|
|
163
|
+
| undefined,
|
|
164
|
+
encryptedThumbnails: { type: ThumbnailType; encryptedData: Uint8Array<ArrayBuffer> }[],
|
|
165
|
+
signal?: AbortSignal,
|
|
166
|
+
): Promise<{ nodeUid: string; nodeRevisionUid: string }> {
|
|
167
|
+
try {
|
|
168
|
+
const result = await this.apiService.uploadSmallFile(
|
|
169
|
+
parentFolderUid,
|
|
170
|
+
{
|
|
171
|
+
armoredEncryptedName: nodeCrypto.encryptedNode.encryptedName,
|
|
172
|
+
hash: nodeCrypto.encryptedNode.hash,
|
|
173
|
+
mediaType: metadata.mediaType ?? 'application/octet-stream',
|
|
174
|
+
armoredNodeKey: nodeCrypto.nodeKeys.encrypted.armoredKey,
|
|
175
|
+
armoredNodePassphrase: nodeCrypto.nodeKeys.encrypted.armoredPassphrase,
|
|
176
|
+
armoredNodePassphraseSignature: nodeCrypto.nodeKeys.encrypted.armoredPassphraseSignature,
|
|
177
|
+
base64ContentKeyPacket: nodeCrypto.contentKey.encrypted.base64ContentKeyPacket,
|
|
178
|
+
armoredContentKeyPacketSignature: nodeCrypto.contentKey.encrypted.armoredContentKeyPacketSignature,
|
|
179
|
+
armoredExtendedAttributes: commitPayload.armoredExtendedAttributes,
|
|
180
|
+
signatureEmail: nodeCrypto.signingKeys.email ?? null,
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
armoredManifestSignature: commitPayload.armoredManifestSignature,
|
|
184
|
+
block: encryptedBlock
|
|
185
|
+
? {
|
|
186
|
+
encryptedData: encryptedBlock.encryptedData,
|
|
187
|
+
armoredSignature: encryptedBlock.armoredSignature,
|
|
188
|
+
verificationToken: encryptedBlock.verificationToken,
|
|
110
189
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
190
|
+
: undefined,
|
|
191
|
+
thumbnails: encryptedThumbnails,
|
|
192
|
+
},
|
|
193
|
+
signal,
|
|
194
|
+
);
|
|
195
|
+
await this.nodesService.notifyChildCreated(parentFolderUid);
|
|
196
|
+
return result;
|
|
197
|
+
} catch (error: unknown) {
|
|
198
|
+
return this.handleConflictError(parentFolderUid, metadata, error, async () => {
|
|
199
|
+
return this.uploadFile(
|
|
200
|
+
parentFolderUid,
|
|
201
|
+
nodeCrypto,
|
|
202
|
+
metadata,
|
|
203
|
+
commitPayload,
|
|
204
|
+
encryptedBlock,
|
|
205
|
+
encryptedThumbnails,
|
|
206
|
+
signal,
|
|
207
|
+
);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
131
211
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
212
|
+
async uploadSmallRevision(
|
|
213
|
+
nodeUid: string,
|
|
214
|
+
nodeCrypto: Pick<NodeCrypto, 'signingKeys'>,
|
|
215
|
+
commitPayload: {
|
|
216
|
+
armoredManifestSignature: string;
|
|
217
|
+
armoredExtendedAttributes: string;
|
|
218
|
+
},
|
|
219
|
+
encryptedBlock:
|
|
220
|
+
| {
|
|
221
|
+
encryptedData: Uint8Array<ArrayBuffer>;
|
|
222
|
+
armoredSignature: string;
|
|
223
|
+
verificationToken: Uint8Array<ArrayBuffer>;
|
|
224
|
+
}
|
|
225
|
+
| undefined,
|
|
226
|
+
encryptedThumbnails: { type: ThumbnailType; encryptedData: Uint8Array<ArrayBuffer> }[],
|
|
227
|
+
signal?: AbortSignal,
|
|
228
|
+
): Promise<{ nodeUid: string; nodeRevisionUid: string }> {
|
|
229
|
+
const node = await this.nodesService.getNode(nodeUid);
|
|
230
|
+
if (!node.activeRevision?.ok) {
|
|
231
|
+
throw new ValidationError(c('Error').t`File has no revision`);
|
|
232
|
+
}
|
|
233
|
+
const result = await this.apiService.uploadSmallRevision(
|
|
234
|
+
nodeUid,
|
|
235
|
+
node.activeRevision.value.uid,
|
|
236
|
+
{
|
|
237
|
+
signatureEmail: nodeCrypto.signingKeys.email ?? null,
|
|
238
|
+
armoredExtendedAttributes: commitPayload.armoredExtendedAttributes,
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
armoredManifestSignature: commitPayload.armoredManifestSignature,
|
|
242
|
+
block: encryptedBlock,
|
|
243
|
+
thumbnails: encryptedThumbnails,
|
|
244
|
+
},
|
|
245
|
+
signal,
|
|
246
|
+
);
|
|
247
|
+
await this.nodesService.notifyNodeChanged(nodeUid);
|
|
248
|
+
return result;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private async handleConflictError(
|
|
252
|
+
parentFolderUid: string,
|
|
253
|
+
metadata: UploadMetadata,
|
|
254
|
+
error: unknown,
|
|
255
|
+
onRetryAfterDraftDeleted: () => Promise<{ nodeUid: string; nodeRevisionUid: string }>,
|
|
256
|
+
): Promise<{ nodeUid: string; nodeRevisionUid: string }> {
|
|
257
|
+
if (error instanceof ValidationError) {
|
|
258
|
+
if (error.code === ErrorCode.ALREADY_EXISTS) {
|
|
259
|
+
this.logger.info(`Node with given name already exists`);
|
|
260
|
+
|
|
261
|
+
const typedDetails = error.details as
|
|
262
|
+
| {
|
|
263
|
+
ConflictLinkID: string;
|
|
264
|
+
ConflictRevisionID?: string;
|
|
265
|
+
ConflictDraftRevisionID?: string;
|
|
266
|
+
ConflictDraftClientUID?: string;
|
|
267
|
+
}
|
|
268
|
+
| undefined;
|
|
269
|
+
|
|
270
|
+
// If the client doesn't specify the client UID, it should
|
|
271
|
+
// never be considered own draft.
|
|
272
|
+
const isOwnDraftConflict =
|
|
273
|
+
typedDetails?.ConflictDraftRevisionID &&
|
|
274
|
+
this.clientUid &&
|
|
275
|
+
typedDetails?.ConflictDraftClientUID === this.clientUid;
|
|
276
|
+
|
|
277
|
+
// If there is existing draft created by this client,
|
|
278
|
+
// automatically delete it and try to create a new one
|
|
279
|
+
// with the same name again.
|
|
280
|
+
if (
|
|
281
|
+
typedDetails?.ConflictDraftRevisionID &&
|
|
282
|
+
(isOwnDraftConflict || metadata.overrideExistingDraftByOtherClient)
|
|
283
|
+
) {
|
|
284
|
+
const existingDraftNodeUid = makeNodeUid(
|
|
285
|
+
splitNodeUid(parentFolderUid).volumeId,
|
|
286
|
+
typedDetails.ConflictLinkID,
|
|
287
|
+
);
|
|
153
288
|
|
|
154
|
-
|
|
289
|
+
let deleteFailed = false;
|
|
290
|
+
try {
|
|
155
291
|
this.logger.warn(
|
|
156
|
-
`
|
|
292
|
+
`Deleting existing draft node ${existingDraftNodeUid} by ${typedDetails.ConflictDraftClientUID}`,
|
|
157
293
|
);
|
|
294
|
+
await this.apiService.deleteDraft(existingDraftNodeUid);
|
|
295
|
+
} catch (deleteDraftError: unknown) {
|
|
296
|
+
// Do not throw, let throw the conflict error.
|
|
297
|
+
deleteFailed = true;
|
|
298
|
+
this.logger.error('Failed to delete existing draft node', deleteDraftError);
|
|
158
299
|
}
|
|
300
|
+
if (!deleteFailed) {
|
|
301
|
+
return onRetryAfterDraftDeleted();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
159
304
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
throw new NodeWithSameNameExistsValidationError(
|
|
165
|
-
error.message,
|
|
166
|
-
error.code,
|
|
167
|
-
existingNodeUid,
|
|
168
|
-
!!typedDetails?.ConflictDraftRevisionID,
|
|
305
|
+
if (isOwnDraftConflict) {
|
|
306
|
+
this.logger.warn(
|
|
307
|
+
`Existing draft conflict by another client ${typedDetails.ConflictDraftClientUID}`,
|
|
169
308
|
);
|
|
170
309
|
}
|
|
310
|
+
|
|
311
|
+
const existingNodeUid = typedDetails
|
|
312
|
+
? makeNodeUid(splitNodeUid(parentFolderUid).volumeId, typedDetails.ConflictLinkID)
|
|
313
|
+
: undefined;
|
|
314
|
+
|
|
315
|
+
throw new NodeWithSameNameExistsValidationError(
|
|
316
|
+
error.message,
|
|
317
|
+
error.code,
|
|
318
|
+
existingNodeUid,
|
|
319
|
+
!!typedDetails?.ConflictDraftRevisionID,
|
|
320
|
+
);
|
|
171
321
|
}
|
|
172
|
-
throw error;
|
|
173
322
|
}
|
|
323
|
+
throw error;
|
|
174
324
|
}
|
|
175
325
|
|
|
176
326
|
async deleteDraftNode(nodeUid: string): Promise<void> {
|