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