all files / rebaser/src/dm/ ve.dm.DocumentStore.js

100% Statements 38/38
100% Branches 8/8
100% Functions 11/11
100% Lines 38/38
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117                                                                                                                                                             
/*!
 * VisualEditor document store class.
 *
 * @copyright 2011-2019 VisualEditor Team and others; see http://ve.mit-license.org
 */
 
/**
 * @constructor
 * @param {Object} storageClient MongoClient-like object; passed as a parameter for testing purposes
 * @param {string} dbName Database name
 * @param {Object} logger Logger class
 * @param {Function} logger.logServerEvent Stringify object argument to log, adding timestamp and server ID properties
 */
ve.dm.DocumentStore = function VeDmDocumentStore( storageClient, dbName, logger ) {
	this.storageClient = storageClient;
	this.dbName = dbName;
	this.logger = logger;
	this.db = null;
	this.collection = null;
	this.startForDoc = new Map();
	this.serverId = null;
};
 
/**
 * @return {Promise} Resolves when connected
 */
ve.dm.DocumentStore.prototype.connect = function () {
	var documentStore = this;
	return this.storageClient.connect().then( function ( client ) {
		var db = client.db( documentStore.dbName );
		documentStore.logger.logServerEvent( { type: 'DocumentStore#connected', dbName: documentStore.dbName }, 'info' );
		documentStore.db = db;
		documentStore.collection = db.collection( 'vedocstore' );
		return documentStore.collection.findOneAndUpdate(
			{ config: 'options' },
			{ $setOnInsert: { serverId: Math.random().toString( 36 ).slice( 2 ) } },
			{ upsert: true, returnOriginal: false }
		);
	} ).then( function ( result ) {
		documentStore.serverId = result.value.serverId;
	} );
};
 
/**
 * @return {Promise} Drops the entire database
 */
ve.dm.DocumentStore.prototype.dropDatabase = function () {
	this.logger.logServerEvent( { type: 'DocumentStore#dropDatabase', dbName: this.dbName }, 'info' );
	return this.db.dropDatabase();
};
 
/**
 * Load a document from storage (creating as empty if absent)
 *
 * @param {string} docName Name of the document
 * @return {Promise} Confirmed document history as a ve.dm.Change
 */
ve.dm.DocumentStore.prototype.load = function ( docName ) {
	var documentStore = this;
	return this.collection.findOneAndUpdate(
		{ docName: docName },
		{ $setOnInsert: { start: 0, transactions: [], stores: [] } },
		{ upsert: true, returnOriginal: false }
	).then( function ( result ) {
		var length = result.value.transactions.length || 0;
		documentStore.logger.logServerEvent( { type: 'DocumentStore#loaded', docName: docName, length: length } );
		documentStore.startForDoc.set( docName, result.value.start + length );
		return ve.dm.Change.static.deserialize( {
			start: 0,
			transactions: result.value.transactions,
			stores: result.value.stores,
			selections: {}
		}, true );
	} );
};
 
/**
 * Save a new change to storage
 *
 * @param {string} docName Name of the document
 * @param {ve.dm.Change} change The new change
 * @return {Promise} Resolves when saved
 */
ve.dm.DocumentStore.prototype.onNewChange = function ( docName, change ) {
	var documentStore = this,
		serializedChange = change.serialize( true ),
		expectedStart = this.startForDoc.get( docName ) || 0;
 
	if ( expectedStart !== serializedChange.start ) {
		return Promise.reject( 'Unmatched starts:', expectedStart, serializedChange.start );
	}
	this.startForDoc.set( docName, serializedChange.start + serializedChange.transactions.length );
	return this.collection.updateOne(
		{ docName: docName },
		{
			$push: {
				transactions: { $each: serializedChange.transactions },
				stores: { $each: serializedChange.stores || serializedChange.transactions.map( function () {
					return null;
				} ) }
			}
		}
	).then( function () {
		documentStore.logger.logServerEvent( {
			type: 'DocumentStore#onNewChange',
			docName: docName,
			start: serializedChange.start,
			length: serializedChange.transactions.length
		} );
	} );
};
 
ve.dm.DocumentStore.prototype.onClose = function () {
	this.logger.logServerEvent( { type: 'DocumentStore#onClose' }, 'info' );
	this.storageClient.close();
};