mirror of
https://github.com/nasa/openmct.git
synced 2025-06-14 21:28:12 +00:00
Better handling of persistence errors (#5576)
* conflict errors in particular
This commit is contained in:
@ -230,6 +230,7 @@ export default class ObjectAPI {
|
|||||||
return result;
|
return result;
|
||||||
}).catch((result) => {
|
}).catch((result) => {
|
||||||
console.warn(`Failed to retrieve ${keystring}:`, result);
|
console.warn(`Failed to retrieve ${keystring}:`, result);
|
||||||
|
this.openmct.notifications.error(`Failed to retrieve object ${keystring}`);
|
||||||
|
|
||||||
delete this.cache[keystring];
|
delete this.cache[keystring];
|
||||||
|
|
||||||
@ -387,7 +388,13 @@ export default class ObjectAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result.catch((error) => {
|
||||||
|
if (error instanceof this.errors.Conflict) {
|
||||||
|
this.openmct.notifications.error(`Conflict detected while saving ${this.makeKeyString(domainObject.identifier)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,6 +7,7 @@ describe("The Object API", () => {
|
|||||||
let openmct = {};
|
let openmct = {};
|
||||||
let mockDomainObject;
|
let mockDomainObject;
|
||||||
const TEST_NAMESPACE = "test-namespace";
|
const TEST_NAMESPACE = "test-namespace";
|
||||||
|
const TEST_KEY = "test-key";
|
||||||
const FIFTEEN_MINUTES = 15 * 60 * 1000;
|
const FIFTEEN_MINUTES = 15 * 60 * 1000;
|
||||||
|
|
||||||
beforeEach((done) => {
|
beforeEach((done) => {
|
||||||
@ -22,7 +23,7 @@ describe("The Object API", () => {
|
|||||||
mockDomainObject = {
|
mockDomainObject = {
|
||||||
identifier: {
|
identifier: {
|
||||||
namespace: TEST_NAMESPACE,
|
namespace: TEST_NAMESPACE,
|
||||||
key: "test-key"
|
key: TEST_KEY
|
||||||
},
|
},
|
||||||
name: "test object",
|
name: "test object",
|
||||||
type: "test-type"
|
type: "test-type"
|
||||||
@ -84,6 +85,31 @@ describe("The Object API", () => {
|
|||||||
expect(mockProvider.create).not.toHaveBeenCalled();
|
expect(mockProvider.create).not.toHaveBeenCalled();
|
||||||
expect(mockProvider.update).not.toHaveBeenCalled();
|
expect(mockProvider.update).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Shows a notification on persistence conflict", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
openmct.notifications.error = jasmine.createSpy('error');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("on create", () => {
|
||||||
|
mockProvider.create.and.returnValue(Promise.reject(new openmct.objects.errors.Conflict("Test Conflict error")));
|
||||||
|
|
||||||
|
return objectAPI.save(mockDomainObject).catch(() => {
|
||||||
|
expect(openmct.notifications.error).toHaveBeenCalledWith(`Conflict detected while saving ${TEST_NAMESPACE}:${TEST_KEY}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it("on update", () => {
|
||||||
|
mockProvider.update.and.returnValue(Promise.reject(new openmct.objects.errors.Conflict("Test Conflict error")));
|
||||||
|
mockDomainObject.persisted = Date.now() - FIFTEEN_MINUTES;
|
||||||
|
mockDomainObject.modified = Date.now();
|
||||||
|
|
||||||
|
return objectAPI.save(mockDomainObject).catch(() => {
|
||||||
|
expect(openmct.notifications.error).toHaveBeenCalledWith(`Conflict detected while saving ${TEST_NAMESPACE}:${TEST_KEY}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -138,21 +164,33 @@ describe("The Object API", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Caches multiple requests for the same object", () => {
|
it("Caches multiple requests for the same object", () => {
|
||||||
|
const promises = [];
|
||||||
expect(mockProvider.get.calls.count()).toBe(0);
|
expect(mockProvider.get.calls.count()).toBe(0);
|
||||||
objectAPI.get(mockDomainObject.identifier);
|
promises.push(objectAPI.get(mockDomainObject.identifier));
|
||||||
expect(mockProvider.get.calls.count()).toBe(1);
|
expect(mockProvider.get.calls.count()).toBe(1);
|
||||||
objectAPI.get(mockDomainObject.identifier);
|
promises.push(objectAPI.get(mockDomainObject.identifier));
|
||||||
expect(mockProvider.get.calls.count()).toBe(1);
|
expect(mockProvider.get.calls.count()).toBe(1);
|
||||||
|
|
||||||
|
return Promise.all(promises);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("applies any applicable interceptors", () => {
|
it("applies any applicable interceptors", () => {
|
||||||
expect(mockDomainObject.changed).toBeUndefined();
|
expect(mockDomainObject.changed).toBeUndefined();
|
||||||
objectAPI.get(mockDomainObject.identifier).then((object) => {
|
|
||||||
|
return objectAPI.get(mockDomainObject.identifier).then((object) => {
|
||||||
expect(object.changed).toBeTrue();
|
expect(object.changed).toBeTrue();
|
||||||
expect(object.alsoChanged).toBeTrue();
|
expect(object.alsoChanged).toBeTrue();
|
||||||
expect(object.shouldNotBeChanged).toBeUndefined();
|
expect(object.shouldNotBeChanged).toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("displays a notification in the event of an error", () => {
|
||||||
|
mockProvider.get.and.returnValue(Promise.reject());
|
||||||
|
|
||||||
|
return objectAPI.get(mockDomainObject.identifier).catch(() => {
|
||||||
|
expect(openmct.notifications.error).toHaveBeenCalledWith(`Failed to retrieve object ${TEST_NAMESPACE}:${TEST_KEY}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -168,7 +206,7 @@ describe("The Object API", () => {
|
|||||||
testObject = {
|
testObject = {
|
||||||
identifier: {
|
identifier: {
|
||||||
namespace: TEST_NAMESPACE,
|
namespace: TEST_NAMESPACE,
|
||||||
key: 'test-key'
|
key: TEST_KEY
|
||||||
},
|
},
|
||||||
name: 'test object',
|
name: 'test object',
|
||||||
type: 'notebook',
|
type: 'notebook',
|
||||||
@ -195,6 +233,8 @@ describe("The Object API", () => {
|
|||||||
"observeObjectChanges"
|
"observeObjectChanges"
|
||||||
]);
|
]);
|
||||||
mockProvider.get.and.returnValue(Promise.resolve(testObject));
|
mockProvider.get.and.returnValue(Promise.resolve(testObject));
|
||||||
|
mockProvider.create.and.returnValue(Promise.resolve(true));
|
||||||
|
mockProvider.update.and.returnValue(Promise.resolve(true));
|
||||||
mockProvider.observeObjectChanges.and.callFake(() => {
|
mockProvider.observeObjectChanges.and.callFake(() => {
|
||||||
callbacks[0](updatedTestObject);
|
callbacks[0](updatedTestObject);
|
||||||
callbacks.splice(0, 1);
|
callbacks.splice(0, 1);
|
||||||
|
@ -217,9 +217,11 @@ class CouchObjectProvider {
|
|||||||
this.indicator.setIndicatorToState(DISCONNECTED);
|
this.indicator.setIndicatorToState(DISCONNECTED);
|
||||||
console.error(error.message);
|
console.error(error.message);
|
||||||
throw new Error(`CouchDB Error - No response"`);
|
throw new Error(`CouchDB Error - No response"`);
|
||||||
}
|
} else {
|
||||||
|
console.error(error.message);
|
||||||
|
|
||||||
console.error(error.message);
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -653,7 +655,6 @@ class CouchObjectProvider {
|
|||||||
let document = new CouchDocument(key, queued.model);
|
let document = new CouchDocument(key, queued.model);
|
||||||
document.metadata.created = Date.now();
|
document.metadata.created = Date.now();
|
||||||
this.request(key, "PUT", document).then((response) => {
|
this.request(key, "PUT", document).then((response) => {
|
||||||
console.log('create check response', key);
|
|
||||||
this.#checkResponse(response, queued.intermediateResponse, key);
|
this.#checkResponse(response, queued.intermediateResponse, key);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
queued.intermediateResponse.reject(error);
|
queued.intermediateResponse.reject(error);
|
||||||
|
Reference in New Issue
Block a user