Compare commits
	
		
			4 Commits
		
	
	
		
			6359-sub-o
			...
			nb-conflic
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					c25095a801 | ||
| 
						 | 
					0e3e32f037 | ||
| 
						 | 
					9f6463105a | ||
| 
						 | 
					317abfe2c1 | 
@@ -75,7 +75,8 @@
 | 
			
		||||
        const TWO_HOURS = ONE_HOUR * 2;
 | 
			
		||||
        const ONE_DAY = ONE_HOUR * 24;
 | 
			
		||||
 | 
			
		||||
        openmct.install(openmct.plugins.LocalStorage());
 | 
			
		||||
        // openmct.install(openmct.plugins.LocalStorage());
 | 
			
		||||
        openmct.install(openmct.plugins.CouchDB("http://localhost:5984/openmct"));
 | 
			
		||||
 | 
			
		||||
        openmct.install(openmct.plugins.example.Generator());
 | 
			
		||||
        openmct.install(openmct.plugins.example.EventGeneratorPlugin());
 | 
			
		||||
 
 | 
			
		||||
@@ -196,20 +196,22 @@ export default class ObjectAPI {
 | 
			
		||||
     * @returns {Promise} a promise which will resolve when the domain object
 | 
			
		||||
     *          has been saved, or be rejected if it cannot be saved
 | 
			
		||||
     */
 | 
			
		||||
    get(identifier, abortSignal) {
 | 
			
		||||
    get(identifier, abortSignal = undefined, forceRemote = false) {
 | 
			
		||||
        let keystring = this.makeKeyString(identifier);
 | 
			
		||||
 | 
			
		||||
        if (this.cache[keystring] !== undefined) {
 | 
			
		||||
            return this.cache[keystring];
 | 
			
		||||
        }
 | 
			
		||||
        if (!forceRemote) {
 | 
			
		||||
            if (this.cache[keystring] !== undefined) {
 | 
			
		||||
                return this.cache[keystring];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        identifier = utils.parseKeyString(identifier);
 | 
			
		||||
            identifier = utils.parseKeyString(identifier);
 | 
			
		||||
 | 
			
		||||
        if (this.isTransactionActive()) {
 | 
			
		||||
            let dirtyObject = this.transaction.getDirtyObject(identifier);
 | 
			
		||||
            if (this.isTransactionActive()) {
 | 
			
		||||
                let dirtyObject = this.transaction.getDirtyObject(identifier);
 | 
			
		||||
 | 
			
		||||
            if (dirtyObject) {
 | 
			
		||||
                return Promise.resolve(dirtyObject);
 | 
			
		||||
                if (dirtyObject) {
 | 
			
		||||
                    return Promise.resolve(dirtyObject);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -399,7 +401,7 @@ export default class ObjectAPI {
 | 
			
		||||
                savedObjectPromise.then(response => {
 | 
			
		||||
                    savedResolve(response);
 | 
			
		||||
                }).catch((error) => {
 | 
			
		||||
                    if (lastPersistedTime !== undefined) {
 | 
			
		||||
                    if (isNewObject) {
 | 
			
		||||
                        this.#mutate(domainObject, 'persisted', lastPersistedTime);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
@@ -412,7 +414,9 @@ export default class ObjectAPI {
 | 
			
		||||
 | 
			
		||||
        return result.catch((error) => {
 | 
			
		||||
            if (error instanceof this.errors.Conflict) {
 | 
			
		||||
                this.openmct.notifications.error(`Conflict detected while saving ${this.makeKeyString(domainObject.identifier)}`);
 | 
			
		||||
                if (!this.SYNCHRONIZED_OBJECT_TYPES.includes(domainObject.type)) {
 | 
			
		||||
                    this.openmct.notifications.error(`Conflict detected while saving ${this.makeKeyString(domainObject.identifier)}`);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            throw error;
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,7 @@
 | 
			
		||||
        <Sidebar
 | 
			
		||||
            ref="sidebar"
 | 
			
		||||
            class="c-notebook__nav c-sidebar c-drawer c-drawer--align-left"
 | 
			
		||||
            :class="[{'is-expanded': showNav}, {'c-drawer--push': !sidebarCoversEntries}, {'c-drawer--overlays': sidebarCoversEntries}]"
 | 
			
		||||
            :class="sidebarClasses"
 | 
			
		||||
            :default-page-id="defaultPageId"
 | 
			
		||||
            :selected-page-id="getSelectedPageId()"
 | 
			
		||||
            :default-section-id="defaultSectionId"
 | 
			
		||||
@@ -270,6 +270,20 @@ export default {
 | 
			
		||||
 | 
			
		||||
            return this.sections[0];
 | 
			
		||||
        },
 | 
			
		||||
        sidebarClasses() {
 | 
			
		||||
            let sidebarClasses = [];
 | 
			
		||||
            if (this.showNav) {
 | 
			
		||||
                sidebarClasses.push('is-expanded');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.sidebarCoversEntries) {
 | 
			
		||||
                sidebarClasses.push('c-drawer--overlays');
 | 
			
		||||
            } else {
 | 
			
		||||
                sidebarClasses.push('c-drawer--push');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return sidebarClasses;
 | 
			
		||||
        },
 | 
			
		||||
        showLockButton() {
 | 
			
		||||
            const entries = getNotebookEntries(this.domainObject, this.selectedSection, this.selectedPage);
 | 
			
		||||
 | 
			
		||||
@@ -300,16 +314,14 @@ export default {
 | 
			
		||||
        window.addEventListener('orientationchange', this.formatSidebar);
 | 
			
		||||
        window.addEventListener('hashchange', this.setSectionAndPageFromUrl);
 | 
			
		||||
        this.filterAndSortEntries();
 | 
			
		||||
        this.unobserveEntries = this.openmct.objects.observe(this.domainObject, '*', this.filterAndSortEntries);
 | 
			
		||||
        this.startObservingEntries();
 | 
			
		||||
    },
 | 
			
		||||
    beforeDestroy() {
 | 
			
		||||
        if (this.unlisten) {
 | 
			
		||||
            this.unlisten();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.unobserveEntries) {
 | 
			
		||||
            this.unobserveEntries();
 | 
			
		||||
        }
 | 
			
		||||
        this.stopObservingEntries();
 | 
			
		||||
 | 
			
		||||
        Object.keys(this.notebookAnnotations).forEach(entryID => {
 | 
			
		||||
            const notebookAnnotationsForEntry = this.notebookAnnotations[entryID];
 | 
			
		||||
@@ -327,6 +339,17 @@ export default {
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        startObservingEntries() {
 | 
			
		||||
            if (this.unobserveEntries) {
 | 
			
		||||
                this.stopObservingEntries();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.unobserveEntries = this.openmct.objects.observe(this.domainObject, '*', this.filterAndSortEntries);
 | 
			
		||||
        },
 | 
			
		||||
        stopObservingEntries() {
 | 
			
		||||
            this.unobserveEntries();
 | 
			
		||||
            delete this.unobserveEntries;
 | 
			
		||||
        },
 | 
			
		||||
        changeSectionPage(newParams, oldParams, changedParams) {
 | 
			
		||||
            if (isNotebookViewType(newParams.view)) {
 | 
			
		||||
                return;
 | 
			
		||||
@@ -749,6 +772,7 @@ export default {
 | 
			
		||||
            return section.id;
 | 
			
		||||
        },
 | 
			
		||||
        async newEntry(embed = null) {
 | 
			
		||||
            this.startTransaction();
 | 
			
		||||
            this.resetSearch();
 | 
			
		||||
            const notebookStorage = this.createNotebookStorageObject();
 | 
			
		||||
            this.updateDefaultNotebook(notebookStorage);
 | 
			
		||||
@@ -835,21 +859,21 @@ export default {
 | 
			
		||||
 | 
			
		||||
            setDefaultNotebookSectionId(defaultNotebookSectionId);
 | 
			
		||||
        },
 | 
			
		||||
        updateEntry(entry) {
 | 
			
		||||
        async updateEntry(entry) {
 | 
			
		||||
            const entries = getNotebookEntries(this.domainObject, this.selectedSection, this.selectedPage);
 | 
			
		||||
            const entryPos = getEntryPosById(entry.id, this.domainObject, this.selectedSection, this.selectedPage);
 | 
			
		||||
            entries[entryPos] = entry;
 | 
			
		||||
 | 
			
		||||
            this.updateEntries(entries);
 | 
			
		||||
            await this.updateEntries(entries);
 | 
			
		||||
        },
 | 
			
		||||
        updateEntries(entries) {
 | 
			
		||||
        async updateEntries(entries) {
 | 
			
		||||
            const configuration = this.domainObject.configuration;
 | 
			
		||||
            const notebookEntries = configuration.entries || {};
 | 
			
		||||
            notebookEntries[this.selectedSection.id][this.selectedPage.id] = entries;
 | 
			
		||||
 | 
			
		||||
            mutateObject(this.openmct, this.domainObject, 'configuration.entries', notebookEntries);
 | 
			
		||||
 | 
			
		||||
            this.saveTransaction();
 | 
			
		||||
            await this.saveTransaction();
 | 
			
		||||
        },
 | 
			
		||||
        getPageIdFromUrl() {
 | 
			
		||||
            return this.openmct.router.getParams().pageId;
 | 
			
		||||
@@ -891,20 +915,36 @@ export default {
 | 
			
		||||
        },
 | 
			
		||||
        startTransaction() {
 | 
			
		||||
            if (!this.openmct.objects.isTransactionActive()) {
 | 
			
		||||
                this.stopObservingEntries();
 | 
			
		||||
                this.transaction = this.openmct.objects.startTransaction();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        async saveTransaction() {
 | 
			
		||||
            if (this.transaction !== undefined) {
 | 
			
		||||
                await this.transaction.commit();
 | 
			
		||||
                this.openmct.objects.endTransaction();
 | 
			
		||||
                try {
 | 
			
		||||
                    await this.transaction.commit();
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                    console.warn('Error saving Notebook transaction', error);
 | 
			
		||||
                } finally {
 | 
			
		||||
                    this.endTransaction();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        async cancelTransaction() {
 | 
			
		||||
            if (this.transaction !== undefined) {
 | 
			
		||||
                await this.transaction.cancel();
 | 
			
		||||
                this.openmct.objects.endTransaction();
 | 
			
		||||
                try {
 | 
			
		||||
                    await this.transaction.cancel();
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                    console.warn('Error canceling Notebook transaction', error);
 | 
			
		||||
                } finally {
 | 
			
		||||
                    this.endTransaction();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        endTransaction() {
 | 
			
		||||
            this.openmct.objects.endTransaction();
 | 
			
		||||
            this.transaction = undefined;
 | 
			
		||||
            this.startObservingEntries();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -74,21 +74,25 @@ async function resolveNotebookTagConflicts(localAnnotation, openmct) {
 | 
			
		||||
 | 
			
		||||
async function resolveNotebookEntryConflicts(localMutable, openmct) {
 | 
			
		||||
    if (localMutable.configuration.entries) {
 | 
			
		||||
        const FORCE_REMOTE = true;
 | 
			
		||||
        const localEntries = structuredClone(localMutable.configuration.entries);
 | 
			
		||||
        const remoteMutable = await openmct.objects.getMutable(localMutable.identifier);
 | 
			
		||||
        applyLocalEntries(remoteMutable, localEntries, openmct);
 | 
			
		||||
        openmct.objects.destroyMutable(remoteMutable);
 | 
			
		||||
        const remoteObject = await openmct.objects.get(localMutable.identifier, undefined, FORCE_REMOTE);
 | 
			
		||||
 | 
			
		||||
        return applyLocalEntries(remoteObject, localEntries, openmct);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function applyLocalEntries(mutable, entries, openmct) {
 | 
			
		||||
function applyLocalEntries(remoteObject, entries, openmct) {
 | 
			
		||||
    let shouldSave = false;
 | 
			
		||||
 | 
			
		||||
    Object.entries(entries).forEach(([sectionKey, pagesInSection]) => {
 | 
			
		||||
        Object.entries(pagesInSection).forEach(([pageKey, localEntries]) => {
 | 
			
		||||
            const remoteEntries = mutable.configuration.entries[sectionKey][pageKey];
 | 
			
		||||
            const remoteEntries = remoteObject.configuration.entries[sectionKey][pageKey];
 | 
			
		||||
            const mergedEntries = [].concat(remoteEntries);
 | 
			
		||||
            let shouldMutate = false;
 | 
			
		||||
            console.log('merging after conflict, remote, local', remoteEntries, entries);
 | 
			
		||||
 | 
			
		||||
            const locallyAddedEntries = _.differenceBy(localEntries, remoteEntries, 'id');
 | 
			
		||||
            const locallyModifiedEntries = _.differenceWith(localEntries, remoteEntries, (localEntry, remoteEntry) => {
 | 
			
		||||
@@ -108,10 +112,17 @@ function applyLocalEntries(mutable, entries, openmct) {
 | 
			
		||||
                    shouldMutate = true;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            console.log('merged entries', mergedEntries);
 | 
			
		||||
            if (shouldMutate) {
 | 
			
		||||
                openmct.objects.mutate(mutable, `configuration.entries.${sectionKey}.${pageKey}`, mergedEntries);
 | 
			
		||||
                shouldSave = true;
 | 
			
		||||
                openmct.objects.mutate(remoteObject, `configuration.entries.${sectionKey}.${pageKey}`, mergedEntries);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (shouldSave) {
 | 
			
		||||
        return openmct.objects.save(remoteObject);
 | 
			
		||||
    } else {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,8 +36,8 @@ export default function () {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let wrappedFunction = openmct.objects.get;
 | 
			
		||||
        openmct.objects.get = function migrate(identifier) {
 | 
			
		||||
            return wrappedFunction.apply(openmct.objects, [identifier])
 | 
			
		||||
        openmct.objects.get = function migrate() {
 | 
			
		||||
            return wrappedFunction.apply(openmct.objects, [...arguments])
 | 
			
		||||
                .then(function (object) {
 | 
			
		||||
                    if (needsMigration(object)) {
 | 
			
		||||
                        migrateObject(object)
 | 
			
		||||
 
 | 
			
		||||
@@ -219,7 +219,12 @@ class CouchObjectProvider {
 | 
			
		||||
                console.error(error.message);
 | 
			
		||||
                throw new Error(`CouchDB Error - No response"`);
 | 
			
		||||
            } else {
 | 
			
		||||
                console.error(error.message);
 | 
			
		||||
                if (!isNotebookOrAnnotationType(body.model)) {
 | 
			
		||||
                    console.error(error.message);
 | 
			
		||||
                } else {
 | 
			
		||||
                    // warn since we handle conflicts for notebooks
 | 
			
		||||
                    console.warn(error.message);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                throw error;
 | 
			
		||||
            }
 | 
			
		||||
@@ -554,8 +559,9 @@ class CouchObjectProvider {
 | 
			
		||||
        let observersForObject = this.observers[keyString];
 | 
			
		||||
 | 
			
		||||
        if (observersForObject) {
 | 
			
		||||
            const FORCE_REMOTE = true;
 | 
			
		||||
            observersForObject.forEach(async (observer) => {
 | 
			
		||||
                const updatedObject = await this.get(identifier);
 | 
			
		||||
                const updatedObject = await this.get(identifier, undefined, FORCE_REMOTE);
 | 
			
		||||
                if (this.isSynchronizedObject(updatedObject)) {
 | 
			
		||||
                    observer(updatedObject);
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user