UNPKG

delta-store

Version:
236 lines (189 loc) 6.95 kB
/** * Created by Papa on 1/7/2016. */ import {GoogleRealtime} from './GoogleRealtime'; import {Subject} from 'rxjs/Subject'; import {ChangeRecord} from '../../ChangeModel'; import {DocumentHandle} from './DocumentHandle'; import {ChangeRecordIterator} from '../../ChangeModel'; export enum Operation { CHANGES_ADDED_BY_OTHERS, // CLEAUP_BY_OWNER, // GET_NEXT_CHANGE } export class GoogleRealtimeAdaptorException { constructor( public message:string, public operation:Operation, public event:gapi.drive.realtime.BaseModelEvent, public exception?:any ) { } } export class GoogleChangeRecordIterator implements ChangeRecordIterator { private nextValue:ChangeRecord; constructor( private changeList:gapi.drive.realtime.CollaborativeList<ChangeRecord>, private event:gapi.drive.realtime.BaseModelEvent, private nextIndex:number = 0 ) { } next():ChangeRecord { if (this.hasNext()) { let nextValue = this.nextValue; this.nextValue = null; this.nextIndex++; return nextValue; } let message = 'Error accessing the changelist at index: ' + this.nextIndex; throw generateException(message, Operation.GET_NEXT_CHANGE, this.event); } hasNext():boolean { if (this.nextValue) { return true; } try { this.nextValue = this.changeList.get(this.nextIndex); return !!this.nextValue; } catch (exception) { return false; } } } let generateException = function ( message:string, operation:Operation, event:gapi.drive.realtime.BaseModelEvent, exception?:any ):GoogleRealtimeAdaptorException { message = message + '. User ID: ' + event.userId + ', Session ID: ' + event.sessionId; return new GoogleRealtimeAdaptorException( message, Operation.CLEAUP_BY_OWNER, event, exception ); }; export class GoogleRealtimeAdaptor { constructor( private googleRealtime:GoogleRealtime ) { } startTest():DocumentHandle { let document = this.googleRealtime.createInMemoryDocument(); let eventHub = this.createDocumentHandle(document); return eventHub; } startNewShare( fileId:string ):Promise<DocumentHandle> { return this.googleRealtime.initializeFile(fileId).then(( document ) => { let eventHub = this.createDocumentHandle(document); return eventHub; }); } private createDocumentHandle( document:gapi.drive.realtime.Document ):DocumentHandle { let changeList = this.googleRealtime.getChangeList(document); let valuesAddedSubject = this.subscribeToChangesAddedByOthers(document); let valuesArchivedSubject = this.subscribeToCleanupByOwner(document, false); let otherChangesSubject = this.subscribeToUnexpectedModifications(changeList, document); return new DocumentHandle(document, changeList, valuesAddedSubject, valuesArchivedSubject, otherChangesSubject); } private subscribeToChangesAddedByOthers( document:gapi.drive.realtime.Document ):Subject<GoogleChangeRecordIterator> { let valuesAddedSubject = new Subject<gapi.drive.realtime.ValuesAddedEvent<ChangeRecord>>(); let changeList = this.googleRealtime.getChangeList(document); this.googleRealtime.subscribeToValuesAdded(changeList, valuesAddedSubject); let changesAddedSubject = new Subject<GoogleChangeRecordIterator>(); valuesAddedSubject.subscribe(( event:gapi.drive.realtime.ValuesAddedEvent<ChangeRecord> ) => { console.log('Changes by others. BaseModelEvent Type: ' + event.type); if (event.isLocal) { return; } if (event.target !== changeList) { throw generateException('List cleaned up on an unexpected target', Operation.CHANGES_ADDED_BY_OTHERS, event); } if (event.movedFromIndex === 0 || event.movedFromIndex || event.movedFromList) { throw generateException('List cleaned up is a move operation', Operation.CHANGES_ADDED_BY_OTHERS, event); } event.stopPropagation(); let iterator = new GoogleChangeRecordIterator(changeList, event, event.index); changesAddedSubject.next(iterator); }); return changesAddedSubject; } private subscribeToCleanupByOwner( document:gapi.drive.realtime.Document, iAmTheOwner:boolean ):Subject<GoogleChangeRecordIterator> { let valuesRemovedSubject = new Subject<gapi.drive.realtime.ValuesRemovedEvent<ChangeRecord>>(); let changeList = this.googleRealtime.getChangeList(document); this.googleRealtime.subscribeToValuesRemoved(changeList, valuesRemovedSubject); let changesRemovedSubject = new Subject<GoogleChangeRecordIterator>(); valuesRemovedSubject.subscribe(( event:gapi.drive.realtime.ValuesRemovedEvent<ChangeRecord> ) => { console.log('Clean-up by owner. BaseModelEvent Type: ' + event.type); if (event.isLocal) { if (iAmTheOwner) { return; } throw generateException('List cleaned up by non-owner', Operation.CLEAUP_BY_OWNER, event); } if (event.target !== changeList) { throw generateException('List cleaned up on an unexpected target', Operation.CLEAUP_BY_OWNER, event); } if (event.movedToIndex === 0 || event.movedToIndex || event.movedToList) { throw generateException('List cleaned up is a move operation', Operation.CLEAUP_BY_OWNER, event); } if (event.index !== 0) { throw generateException('List cleaned up from invalid index: ' + event.index, Operation.CLEAUP_BY_OWNER, event); } event.stopPropagation(); let iterator = new GoogleChangeRecordIterator(changeList, event); changesRemovedSubject.next(iterator); }); return changesRemovedSubject; } private subscribeToUnexpectedModifications( changeList:gapi.drive.realtime.CollaborativeList<ChangeRecord>, document:gapi.drive.realtime.Document ):Subject<GoogleRealtimeAdaptorException> { let valuesRemovedSubject = new Subject<gapi.drive.realtime.ObjectChangedEvent>(); this.googleRealtime.subscribeToAnyObjectChanged(document, valuesRemovedSubject); let changesRemovedSubject = new Subject<GoogleRealtimeAdaptorException>(); valuesRemovedSubject.subscribe(( event ) => { let message = 'Unexpected change - '; if (!event.events) { message += ' target events not found'; } else if (event.events.length !== 1) { message += ' unexpected number of target events: ' + event.events.length; } else { let causingEvent = event.events[0]; if (causingEvent.target !== changeList) { message += ' unexpected target'; } switch (causingEvent.type) { case gapi.drive.realtime.EventType.VALUES_ADDED: case gapi.drive.realtime.EventType.VALUES_REMOVED: return; default: message += 'unexpected event type: ' + causingEvent.type; } } let exception = new GoogleRealtimeAdaptorException(message, null, event); changesRemovedSubject.next(exception); }); return changesRemovedSubject; } openShare( fileId:string ):Promise<DocumentHandle> { return this.googleRealtime.loadFile(fileId).then(( document ) => { let documentHandle = this.createDocumentHandle(document); return documentHandle; }); } }