google-drive-mock 1.0.8 → 1.0.9
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/routes/v2.js +8 -0
- package/package.json +1 -1
- package/src/routes/v2.ts +9 -0
- package/test/v2_upload.test.ts +99 -0
package/dist/routes/v2.js
CHANGED
|
@@ -391,6 +391,14 @@ const createV2Router = (config) => {
|
|
|
391
391
|
res.status(404).json({ error: { code: 404, message: "File not found" } });
|
|
392
392
|
return;
|
|
393
393
|
}
|
|
394
|
+
const ifMatchHeader = req.headers['if-match'];
|
|
395
|
+
const ifMatch = Array.isArray(ifMatchHeader) ? ifMatchHeader[0] : ifMatchHeader;
|
|
396
|
+
if (ifMatch && ifMatch !== '*' && ifMatch !== existingFile.etag) {
|
|
397
|
+
if (ifMatch !== existingFile.etag && ifMatch !== `"${existingFile.etag}"`) {
|
|
398
|
+
res.status(412).json({ error: { code: 412, message: "Precondition Failed" } });
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
394
402
|
const uploadType = req.query.uploadType;
|
|
395
403
|
if (uploadType === 'media') {
|
|
396
404
|
const rawBody = req.body;
|
package/package.json
CHANGED
package/src/routes/v2.ts
CHANGED
|
@@ -454,6 +454,15 @@ export const createV2Router = (config: AppConfig) => {
|
|
|
454
454
|
return;
|
|
455
455
|
}
|
|
456
456
|
|
|
457
|
+
const ifMatchHeader = req.headers['if-match'];
|
|
458
|
+
const ifMatch = Array.isArray(ifMatchHeader) ? ifMatchHeader[0] : ifMatchHeader;
|
|
459
|
+
if (ifMatch && ifMatch !== '*' && ifMatch !== existingFile.etag) {
|
|
460
|
+
if (ifMatch !== existingFile.etag && ifMatch !== `"${existingFile.etag}"`) {
|
|
461
|
+
res.status(412).json({ error: { code: 412, message: "Precondition Failed" } });
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
457
466
|
const uploadType = req.query.uploadType as string;
|
|
458
467
|
|
|
459
468
|
if (uploadType === 'media') {
|
package/test/v2_upload.test.ts
CHANGED
|
@@ -139,4 +139,103 @@ describe('V2 Upload Features', () => {
|
|
|
139
139
|
});
|
|
140
140
|
expect(await contentRes.text()).toBe(newContent);
|
|
141
141
|
});
|
|
142
|
+
it('should respect If-Match header in V2 media upload (PUT)', async () => {
|
|
143
|
+
// 1. Create file
|
|
144
|
+
const createRes = await fetch(`${config.baseUrl}/drive/v2/files`, {
|
|
145
|
+
method: 'POST',
|
|
146
|
+
headers: {
|
|
147
|
+
'Authorization': `Bearer ${config.token}`,
|
|
148
|
+
'Content-Type': 'application/json'
|
|
149
|
+
},
|
|
150
|
+
body: JSON.stringify({ title: 'ETag Media Test', mimeType: 'text/plain' })
|
|
151
|
+
});
|
|
152
|
+
const file = await createRes.json();
|
|
153
|
+
const fileId = file.id;
|
|
154
|
+
const etag = file.etag;
|
|
155
|
+
|
|
156
|
+
const invalidEtag = etag ? etag.replace(/.$/, '0') : '"wrong-etag"';
|
|
157
|
+
|
|
158
|
+
// 2. Update with WRONG ETag
|
|
159
|
+
const wrongEtagRes = await fetch(`${config.baseUrl}/upload/drive/v2/files/${fileId}?uploadType=media`, {
|
|
160
|
+
method: 'PUT',
|
|
161
|
+
headers: {
|
|
162
|
+
'Authorization': `Bearer ${config.token}`,
|
|
163
|
+
'Content-Type': 'text/plain',
|
|
164
|
+
'If-Match': invalidEtag
|
|
165
|
+
},
|
|
166
|
+
body: 'Should Fail'
|
|
167
|
+
});
|
|
168
|
+
expect(wrongEtagRes.status).toBe(412);
|
|
169
|
+
|
|
170
|
+
// 3. Update with CORRECT ETag
|
|
171
|
+
const correctEtagRes = await fetch(`${config.baseUrl}/upload/drive/v2/files/${fileId}?uploadType=media`, {
|
|
172
|
+
method: 'PUT',
|
|
173
|
+
headers: {
|
|
174
|
+
'Authorization': `Bearer ${config.token}`,
|
|
175
|
+
'Content-Type': 'text/plain',
|
|
176
|
+
'If-Match': etag
|
|
177
|
+
},
|
|
178
|
+
body: 'Updated with ETag'
|
|
179
|
+
});
|
|
180
|
+
expect(correctEtagRes.status).toBe(200);
|
|
181
|
+
|
|
182
|
+
// Verify Content
|
|
183
|
+
const contentRes = await fetch(`${config.baseUrl}/drive/v2/files/${fileId}?alt=media`, {
|
|
184
|
+
headers: { 'Authorization': `Bearer ${config.token}` }
|
|
185
|
+
});
|
|
186
|
+
expect(await contentRes.text()).toBe('Updated with ETag');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should respect If-Match header in V2 multipart upload (PUT)', async () => {
|
|
190
|
+
// 1. Create file
|
|
191
|
+
const createRes = await fetch(`${config.baseUrl}/drive/v2/files`, {
|
|
192
|
+
method: 'POST',
|
|
193
|
+
headers: {
|
|
194
|
+
'Authorization': `Bearer ${config.token}`,
|
|
195
|
+
'Content-Type': 'application/json'
|
|
196
|
+
},
|
|
197
|
+
body: JSON.stringify({ title: 'ETag Multipart Test', mimeType: 'text/plain' })
|
|
198
|
+
});
|
|
199
|
+
const file = await createRes.json();
|
|
200
|
+
const fileId = file.id;
|
|
201
|
+
const etag = file.etag;
|
|
202
|
+
|
|
203
|
+
const boundary = 'foo_bar_baz';
|
|
204
|
+
const delimiter = `\r\n--${boundary}\r\n`;
|
|
205
|
+
const closeDelim = `\r\n--${boundary}--`;
|
|
206
|
+
const body = delimiter +
|
|
207
|
+
'Content-Type: application/json\r\n\r\n' +
|
|
208
|
+
JSON.stringify({ title: 'Updated Title' }) +
|
|
209
|
+
delimiter +
|
|
210
|
+
'Content-Type: text/plain\r\n\r\n' +
|
|
211
|
+
'Multipart Update' +
|
|
212
|
+
closeDelim;
|
|
213
|
+
|
|
214
|
+
const invalidEtag = etag ? etag.replace(/.$/, '0') : '"wrong-etag"';
|
|
215
|
+
|
|
216
|
+
// 2. Update with WRONG ETag
|
|
217
|
+
const wrongEtagRes = await fetch(`${config.baseUrl}/upload/drive/v2/files/${fileId}?uploadType=multipart`, {
|
|
218
|
+
method: 'PUT',
|
|
219
|
+
headers: {
|
|
220
|
+
'Authorization': `Bearer ${config.token}`,
|
|
221
|
+
'Content-Type': `multipart/related; boundary="${boundary}"`,
|
|
222
|
+
'If-Match': invalidEtag
|
|
223
|
+
},
|
|
224
|
+
body: body
|
|
225
|
+
});
|
|
226
|
+
expect(wrongEtagRes.status).toBe(412);
|
|
227
|
+
|
|
228
|
+
// 3. Update with CORRECT ETag
|
|
229
|
+
const correctEtagRes = await fetch(`${config.baseUrl}/upload/drive/v2/files/${fileId}?uploadType=multipart`, {
|
|
230
|
+
method: 'PUT',
|
|
231
|
+
headers: {
|
|
232
|
+
'Authorization': `Bearer ${config.token}`,
|
|
233
|
+
'Content-Type': `multipart/related; boundary="${boundary}"`,
|
|
234
|
+
'If-Match': etag
|
|
235
|
+
},
|
|
236
|
+
body: body
|
|
237
|
+
});
|
|
238
|
+
expect(correctEtagRes.status).toBe(200);
|
|
239
|
+
expect((await correctEtagRes.json()).title).toBe('Updated Title');
|
|
240
|
+
});
|
|
142
241
|
});
|