Compare commits

...

1 Commits

Author SHA1 Message Date
Nicolas Humbert f91f75c97a ARSN-202 Overwrite master version metadata 2022-05-13 14:30:12 -07:00
4 changed files with 191 additions and 0 deletions

View File

@ -866,6 +866,42 @@ class MongoClientInterface {
}); });
} }
/**
* Update master with specific version if provided.
* @param {Object} c bucket collection
* @param {String} bucketName bucket name
* @param {String} objName object name
* @param {Object} objVal object metadata
* @param {Object} params params
* @param {String} params.masterVersionId master version id
* @param {Object} log logger
* @param {Function} cb callback
* @return {undefined}
*/
putObjectMasterVer(c, bucketName, objName, objVal, params, log, cb) {
const { masterVersionId, vFormat } = params;
if (masterVersionId) {
// eslint-disable-next-line no-param-reassign
objVal.versionId = masterVersionId;
}
const masterKey = formatMasterKey(objName, vFormat);
c.update({
_id: masterKey,
}, {
_id: masterKey,
value: objVal,
}, {
upsert: true,
}, err => {
if (err) {
log.error('putObjectNoVer: error updating master with specific version', { error: err.message });
return cb(errors.InternalError);
}
const versionId = objVal.versionId || '';
return cb(null, `{"versionId": "${versionId}"}`);
});
}
/** /**
* Returns the putObjectVerCase function to use * Returns the putObjectVerCase function to use
* depending on params * depending on params
@ -873,6 +909,9 @@ class MongoClientInterface {
* @return {Function} suitable putObjectVerCase function * @return {Function} suitable putObjectVerCase function
*/ */
getPutObjectVerStrategy(params) { getPutObjectVerStrategy(params) {
if (params.masterVersionId || params.masterVersionId === '') {
return this.putObjectMasterVer;
}
if (params.versionId === '') { if (params.versionId === '') {
return this.putObjectVerCase2; return this.putObjectVerCase2;
} else if (params.versionId) { } else if (params.versionId) {

View File

@ -251,6 +251,7 @@ class VersioningRequestProcessor {
put(request, logger, callback) { put(request, logger, callback) {
const { db, key, value, options } = request; const { db, key, value, options } = request;
// valid combinations of versioning options: // valid combinations of versioning options:
// - masterVersionId: update master version.
// - !versioning && !versionId: normal non-versioning put // - !versioning && !versionId: normal non-versioning put
// - versioning && !versionId: create a new version // - versioning && !versionId: create a new version
// - versionId: update (PUT/DELETE) an existing version, and // - versionId: update (PUT/DELETE) an existing version, and
@ -261,6 +262,8 @@ class VersioningRequestProcessor {
(options.versioning || options.versioning === ''); (options.versioning || options.versioning === '');
const versionId = options && const versionId = options &&
(options.versionId || options.versionId === ''); (options.versionId || options.versionId === '');
const masterVersionId = options &&
(options.masterVersionId || options.masterVersionId === '');
const versioningCb = (err, array, vid) => { const versioningCb = (err, array, vid) => {
if (err) { if (err) {
@ -270,6 +273,9 @@ class VersioningRequestProcessor {
logger, err => callback(err, `{"versionId":"${vid}"}`)); logger, err => callback(err, `{"versionId":"${vid}"}`));
}; };
if (masterVersionId) {
return this.processMasterUpdate(request, logger, versioningCb);
}
if (versionId) { if (versionId) {
return this.processVersionSpecificPut(request, logger, return this.processVersionSpecificPut(request, logger,
versioningCb); versioningCb);
@ -306,6 +312,30 @@ class VersioningRequestProcessor {
return callback(null, ops, versionId); return callback(null, ops, versionId);
} }
/**
* Processes a version specific putObject request.
* This will update the master version.
*
* @param {object} request - the request in original
* RepdConnection format { db, key
* [, value][, type], method, options }
* @param {object} logger - logger
* @param {function} callback - expect callback(err, batch, versionId)
* @return {any} - to finish the call
*/
processMasterUpdate(request, logger, callback) {
const { key } = request;
const { masterVersionId } = request.options;
let value;
if (masterVersionId === '') {
value = request.value;
} else {
// if masterVersionId, use it as the version id.
value = Version.appendVersionId(request.value, masterVersionId);
}
return callback(null, [{ key, value }], masterVersionId);
}
/** /**
* Processes a version specific putObject request. This will create a batch * Processes a version specific putObject request. This will create a batch
* of operations for updating the target version, and the master version if * of operations for updating the target version, and the master version if

View File

@ -45,6 +45,36 @@ describe('MongoClientInterface:putObject', () => {
return done(); return done();
}); });
it('Should call putObjectMasterVer with correct params', () => {
// Stubbing functions
const putObjectMasterVer = sinon.spy();
sinon.stub(client, 'getBucketVFormat').callsFake((bucketName, log, cb) => cb(null, 'v0'));
sinon.stub(client, 'putObjectMasterVer').callsFake(putObjectMasterVer);
// checking if function called with correct params
const params = {
masterVersionId: '1234',
vFormat: 'v0',
};
client.putObject('example-bucket', 'example-object', {}, params, {}, {});
const args = [null, 'example-bucket', 'example-object', {}, params, {}, {}];
assert(putObjectMasterVer.calledOnceWith(...args));
});
it('Should call putObjectMasterVer with empty masterVersionId', () => {
// Stubbing functions
const putObjectMasterVer = sinon.spy();
sinon.stub(client, 'getBucketVFormat').callsFake((bucketName, log, cb) => cb(null, 'v0'));
sinon.stub(client, 'putObjectMasterVer').callsFake(putObjectMasterVer);
// checking if function called with correct params
const params = {
masterVersionId: '',
vFormat: 'v0',
};
client.putObject('example-bucket', 'example-object', {}, params, {}, {});
const args = [null, 'example-bucket', 'example-object', {}, params, {}, {}];
assert(putObjectMasterVer.calledOnceWith(...args));
});
it('Should call putObjectVerCase1 with correct params', done => { it('Should call putObjectVerCase1 with correct params', done => {
// Stubbing functions // Stubbing functions
const putObjectVerCase1Spy = sinon.spy(); const putObjectVerCase1Spy = sinon.spy();
@ -302,6 +332,65 @@ describe('MongoClientInterface:putObjectVerCase2', () => {
}); });
}); });
describe('MongoClientInterface:putObjectMasterVer', () => {
let client;
beforeAll(done => {
client = new MongoClientInterface({});
return done();
});
beforeEach(done => {
sinon.stub(utils, 'formatMasterKey').callsFake(() => 'example-master-key');
return done();
});
afterEach(done => {
sinon.restore();
return done();
});
it('should return empty versionId if empty masterVersionId provided', done => {
const collection = {
update: (filter, update, params, cb) => cb(null),
};
const params = {
masterVersionId: '',
};
client.putObjectMasterVer(collection, 'example-bucket', 'example-object', {}, params, logger, (err, res) => {
assert.deepStrictEqual(err, null);
const expectedRes = '{"versionId": ""}';
assert.deepStrictEqual(res, expectedRes);
return done();
});
});
it('should return the updated object versionId', done => {
const collection = {
update: (filter, update, params, cb) => cb(null),
};
const params = {
masterVersionId: '1234',
};
client.putObjectMasterVer(collection, 'example-bucket', 'example-object', {}, params, logger, (err, res) => {
assert.deepStrictEqual(err, null);
const expectedRes = '{"versionId": "1234"}';
assert.deepStrictEqual(res, expectedRes);
return done();
});
});
it('should fail when update fails', done => {
const collection = {
update: (filter, update, params, cb) => cb(errors.InternalError),
};
client.putObjectMasterVer(collection, 'example-bucket', 'example-object', {}, {}, logger, err => {
assert.deepStrictEqual(err, errors.InternalError);
return done();
});
});
});
describe('MongoClientInterface:putObjectVerCase3', () => { describe('MongoClientInterface:putObjectVerCase3', () => {
let client; let client;

View File

@ -219,4 +219,37 @@ describe('test VSP', () => {
}], }],
done); done);
}); });
it('should update master when using masterVersionId', done => {
async.waterfall([next => {
const request = {
db: 'foo',
key: 'bar',
value: '{"qux":"quz"}',
options: { versioning: true },
};
vsp.put(request, logger, next);
},
(res, next) => {
const v1 = Version.from(res).getVersionId();
const request = {
db: 'foo',
key: 'bar',
value: '{"qux":"quz2"}',
options: { masterVersionId: v1 },
};
vsp.put(request, logger, next);
},
(res, next) => {
const request = {
db: 'foo',
key: 'bar',
};
vsp.get(request, logger, next);
},
(res, next) => {
assert.strictEqual(JSON.parse(res).qux, 'quz2');
next();
}], done);
});
}); });