Compare commits

...

4 Commits

Author SHA1 Message Date
Jamie V
c25095a801 observin after end of transaction 2022-12-16 15:03:00 -08:00
Jamie V
0e3e32f037 semi colon 2022-12-16 14:10:55 -08:00
Jamie V
9f6463105a removing some debug code 2022-12-16 14:04:41 -08:00
Jamie V
317abfe2c1 wip 2022-12-16 13:24:45 -08:00
6 changed files with 98 additions and 36 deletions

View File

@@ -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());

View File

@@ -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;

View File

@@ -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();
}
}
};

View File

@@ -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;
}
}

View File

@@ -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)

View File

@@ -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);
}