Skip to content

Commit f93cf9c

Browse files
committed
ETags in folder listings match what remotestorage.js expects
1 parent 388e065 commit f93cf9c

File tree

2 files changed

+74
-12
lines changed

2 files changed

+74
-12
lines changed

lib/routes/S3_store_router.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ module.exports = function ({ endPoint = 'play.min.io', accessKey = 'Q3AM3UQ867SP
208208
const putETag = await putBlob(bucketName, s3Path, contentType, contentLength, req);
209209

210210
const documentMetadata = {
211-
ETag: putETag,
211+
ETag: stripQuotes(putETag),
212212
'Content-Type': contentType,
213213
...(contentLength >= 0 ? { 'Content-Length': contentLength } : null),
214214
'Last-Modified': new Date().toUTCString()
@@ -700,8 +700,8 @@ module.exports = function ({ endPoint = 'play.min.io', accessKey = 'Q3AM3UQ867SP
700700
} else { // item is folder
701701
const itemName = basename(itemPath) + '/';
702702
if (metadata) { // folder item exists
703-
if (parent.items[itemName]?.ETag !== metadata) {
704-
parent.items[itemName] = { ETag: metadata };
703+
if (parent.items[itemName]?.ETag !== stripQuotes(metadata)) {
704+
parent.items[itemName] = { ETag: stripQuotes(metadata) };
705705
didChangeParent = true;
706706
}
707707
} else { // folder item does not exist
@@ -845,3 +845,7 @@ class S3RequestLogger extends NodeHttpHandler {
845845
});
846846
}
847847
}
848+
849+
function stripQuotes (ETag) {
850+
return ETag.replace(/^"|^W\/"|"$/g, '');
851+
}

spec/store_handler.spec.js

+67-9
Original file line numberDiff line numberDiff line change
@@ -374,15 +374,30 @@ module.exports.shouldStoreStreams = function () {
374374
expect(getRes1.get('ETag')).to.match(/^".{6,128}"$/);
375375
const folder = JSON.parse(getRes1._getBuffer().toString());
376376
expect(folder['@context']).to.equal('http://remotestorage.io/spec/folder-description');
377-
expect(folder.items['yellow-red'].ETag).to.equal(putRes1.get('ETag'));
377+
expect(folder.items['yellow-red'].ETag).to.equal(stripQuotes(putRes1.get('ETag')));
378378
expect(folder.items['yellow-red']['Content-Type']).to.equal('text/csv');
379379
expect(folder.items['yellow-red']['Content-Length']).to.equal(content1.length);
380380
expect(Date.now() - new Date(folder.items['yellow-red']['Last-Modified'])).to.be.lessThan(9_000);
381-
expect(folder.items['blue-green'].ETag).to.equal(putRes2.get('ETag'));
381+
expect(folder.items['blue-green'].ETag).to.equal(stripQuotes(putRes2.get('ETag')));
382382
expect(folder.items['blue-green']['Content-Type']).to.equal('text/n3');
383383
expect(folder.items['blue-green']['Content-Length']).to.equal(content2.length);
384384
expect(Date.now() - new Date(folder.items['blue-green']['Last-Modified'])).to.be.lessThan(9_000);
385-
expect(folder.items['subfolder/'].ETag).to.match(/^".{6,128}"$/);
385+
expect(folder.items['subfolder/'].ETag).to.match(/^.{6,128}$/);
386+
387+
const [_subfolderReq, subfolderRes] = await callMiddleware(this.handler, {
388+
method: 'GET',
389+
url: `/${this.userIdStore}/color-category/color-folder/subfolder/`
390+
});
391+
expect(subfolderRes.statusCode).to.equal(200);
392+
expect(subfolderRes.get('Content-Type')).to.equal('application/ld+json');
393+
expect(subfolderRes.get('ETag')).to.match(/^".{6,128}"$/);
394+
expect(stripQuotes(subfolderRes.get('ETag'))).to.equal(folder.items['subfolder/'].ETag);
395+
const subfolder = JSON.parse(subfolderRes._getBuffer().toString());
396+
expect(subfolder['@context']).to.equal('http://remotestorage.io/spec/folder-description');
397+
expect(subfolder.items['purple-ultraviolet'].ETag).to.equal(stripQuotes(putRes3.get('ETag')));
398+
expect(subfolder.items['purple-ultraviolet']['Content-Type']).to.equal('text/plain');
399+
expect(subfolder.items['purple-ultraviolet']['Content-Length']).to.equal(content3.length);
400+
expect(Date.now() - new Date(subfolder.items['purple-ultraviolet']['Last-Modified'])).to.be.lessThan(9_000);
386401

387402
const [_getReq2, getRes2] = await callMiddleware(this.handler, {
388403
method: 'GET',
@@ -391,6 +406,45 @@ module.exports.shouldStoreStreams = function () {
391406
});
392407
expect(getRes2.statusCode).to.equal(304);
393408
expect(getRes2._getBuffer().toString()).to.equal('');
409+
410+
const content3changed = 'plum & not visible';
411+
const [_putReq4, putRes4] = await callMiddleware(this.handler, {
412+
method: 'PUT',
413+
url: `/${this.userIdStore}/color-category/color-folder/subfolder/purple-ultraviolet`,
414+
headers: { 'Content-Length': content3changed.length, 'Content-Type': 'text/plain' },
415+
body: content3changed
416+
});
417+
expect(putRes4.statusCode).to.equal(200);
418+
expect(putRes4.get('ETag')).to.match(/^".{6,128}"$/);
419+
expect(putRes4.get('ETag')).not.to.equal(putRes3.get('ETag'));
420+
expect(putRes4._getBuffer().toString()).to.equal('');
421+
422+
const [_folderReq2, folderRes2] = await callMiddleware(this.handler, {
423+
method: 'GET',
424+
url: `/${this.userIdStore}/color-category/color-folder/`,
425+
headers: { 'If-None-Match': getRes1.get('ETag') }
426+
});
427+
expect(folderRes2.statusCode).to.equal(200);
428+
expect(folderRes2.get('ETag')).to.match(/^".{6,128}"$/);
429+
expect(folderRes2.get('ETag')).not.to.equal(getRes1.get('ETag'));
430+
const folderChanged = JSON.parse(folderRes2._getBuffer().toString());
431+
expect(folderChanged.items['yellow-red'].ETag).to.equal(stripQuotes(putRes1.get('ETag')));
432+
expect(folderChanged.items['blue-green'].ETag).to.equal(stripQuotes(putRes2.get('ETag')));
433+
expect(folderChanged.items['subfolder/'].ETag).to.match(/^.{6,128}$/);
434+
expect(folderChanged.items['subfolder/'].ETag).not.to.equal(folder.items['subfolder/'].ETag);
435+
436+
const [_subfolderReq2, subfolderRes2] = await callMiddleware(this.handler, {
437+
method: 'GET',
438+
url: `/${this.userIdStore}/color-category/color-folder/subfolder/`
439+
});
440+
expect(subfolderRes2.statusCode).to.equal(200);
441+
expect(subfolderRes2.get('ETag')).to.match(/^".{6,128}"$/);
442+
expect(stripQuotes(subfolderRes2.get('ETag'))).not.to.equal(folder.items['subfolder/'].ETag);
443+
expect(stripQuotes(subfolderRes2.get('ETag'))).to.equal(folderChanged.items['subfolder/'].ETag);
444+
const subfolderChanged = JSON.parse(subfolderRes2._getBuffer().toString());
445+
expect(subfolderChanged.items['purple-ultraviolet'].ETag).not.to.equal(stripQuotes(putRes3.get('ETag')));
446+
expect(subfolderChanged.items['purple-ultraviolet'].ETag).to.equal(stripQuotes(putRes4.get('ETag')));
447+
expect(Date.now() - new Date(subfolderChanged.items['purple-ultraviolet']['Last-Modified'])).to.be.lessThan(9_000);
394448
});
395449

396450
it('returns folder when If-None-Match has an old ETag', async function () {
@@ -415,7 +469,7 @@ module.exports.shouldStoreStreams = function () {
415469
expect(getRes.get('ETag')).to.match(/^".{6,128}"$/);
416470
const folder = JSON.parse(getRes._getBuffer().toString());
417471
expect(folder['@context']).to.equal('http://remotestorage.io/spec/folder-description');
418-
expect(folder.items.mud.ETag).to.equal(putRes.get('ETag'));
472+
expect(folder.items.mud.ETag).to.equal(stripQuotes(putRes.get('ETag')));
419473
expect(folder.items.mud['Content-Type']).to.equal('text/vnd.qq');
420474
expect(folder.items.mud['Content-Length']).to.equal(content.length);
421475
expect(Date.now() - new Date(folder.items.mud['Last-Modified'])).to.be.lessThan(9_000);
@@ -853,7 +907,7 @@ module.exports.shouldStoreStreams = function () {
853907
const folder1 = JSON.parse(folderRes1._getBuffer().toString());
854908
expect(folder1.items.qux['Content-Length']).to.be.equal(content.length);
855909
expect(folder1.items.qux['Content-Type']).to.be.equal('text/example');
856-
expect(folder1.items.qux.ETag).to.be.equal(putRes.get('ETag'));
910+
expect(folder1.items.qux.ETag).to.be.equal(stripQuotes(putRes.get('ETag')));
857911
expect(Date.now() - new Date(folder1.items.qux['Last-Modified'])).to.be.lessThan(5000);
858912

859913
const [_folderReq2, folderRes2] = await callMiddleware(this.handler, { method: 'GET', url: `/${this.userIdStore}/photos/foo/` });
@@ -863,7 +917,7 @@ module.exports.shouldStoreStreams = function () {
863917
const folder2 = JSON.parse(folderRes2._getBuffer().toString());
864918
expect(folder2.items['bar/']['Content-Length']).to.be.undefined;
865919
expect(folder2.items['bar/']['Content-Type']).to.be.undefined;
866-
expect(folder2.items['bar/'].ETag).to.be.equal(folderRes1.get('ETag'));
920+
expect(folder2.items['bar/'].ETag).to.be.equal(stripQuotes(folderRes1.get('ETag')));
867921
expect(folder2.items['bar/']['Last-Modified']).to.be.undefined;
868922

869923
const [_folderReq3, folderRes3] = await callMiddleware(this.handler, { method: 'GET', url: `/${this.userIdStore}/photos/` });
@@ -873,7 +927,7 @@ module.exports.shouldStoreStreams = function () {
873927
const folder3 = JSON.parse(folderRes3._getBuffer().toString());
874928
expect(folder3.items['foo/']['Content-Length']).to.be.undefined;
875929
expect(folder3.items['foo/']['Content-Type']).to.be.undefined;
876-
expect(folder3.items['foo/'].ETag).to.be.equal(folderRes2.get('ETag'));
930+
expect(folder3.items['foo/'].ETag).to.be.equal(stripQuotes(folderRes2.get('ETag')));
877931
expect(folder3.items['foo/']['Last-Modified']).to.be.undefined;
878932

879933
const [_folderReq4, folderRes4] = await callMiddleware(this.handler, { method: 'GET', url: `/${this.userIdStore}/` });
@@ -883,7 +937,7 @@ module.exports.shouldStoreStreams = function () {
883937
const folder4 = JSON.parse(folderRes4._getBuffer().toString());
884938
expect(folder4.items['photos/']['Content-Length']).to.be.undefined;
885939
expect(folder4.items['photos/']['Content-Type']).to.be.undefined;
886-
expect(folder4.items['photos/'].ETag).to.be.equal(folderRes3.get('ETag'));
940+
expect(folder4.items['photos/'].ETag).to.be.equal(stripQuotes(folderRes3.get('ETag')));
887941
expect(folder4.items['photos/']['Last-Modified']).to.be.undefined;
888942
});
889943

@@ -1296,7 +1350,7 @@ module.exports.shouldStoreStreams = function () {
12961350
expect(getRes4.get('ETag')).to.match(/^".{6,128}"$/);
12971351
const folder4 = JSON.parse(getRes4._getBuffer().toString());
12981352
expect(folder4['@context']).to.equal('http://remotestorage.io/spec/folder-description');
1299-
expect(folder4.items['europe/'].ETag).to.match(/^".{6,128}"$/);
1353+
expect(folder4.items['europe/'].ETag).to.match(/^.{6,128}$/);
13001354

13011355
const [_req2, res2, next2] = await callMiddleware(this.handler, {
13021356
method: 'DELETE',
@@ -1449,3 +1503,7 @@ module.exports.shouldStoreStreams = function () {
14491503
});
14501504
});
14511505
};
1506+
1507+
function stripQuotes (ETag) {
1508+
return ETag.replace(/^"|^W\/"|"$/g, '');
1509+
}

0 commit comments

Comments
 (0)