Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nasa/openmct.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJamie V <jamie.j.vigliotta@nasa.gov>2022-09-30 23:47:10 +0300
committerGitHub <noreply@github.com>2022-09-30 23:47:10 +0300
commit8c92178895a5ba237e6e2e011630767bd3535a68 (patch)
tree8f8bc90b96fd8e99f3cd7dc46d370630dd77cd3d
parent35bbebbbc74239abccf94d32048f46b25eae8d90 (diff)
[User Attribution] "createdBy" and "modifiedBy" fields for domainObjects (#5741)
* Implementation of user attribution of object changes * Adds created date to object creation * Updating remove action to wait for save before navigationg Co-authored-by: Andrew Henry <akhenry@gmail.com>
-rw-r--r--src/api/annotation/AnnotationAPISpec.js1
-rw-r--r--src/api/objects/ObjectAPI.js18
-rw-r--r--src/api/objects/ObjectAPISpec.js35
-rw-r--r--src/plugins/plan/pluginSpec.js2
-rw-r--r--src/plugins/remove/RemoveAction.js8
-rw-r--r--src/ui/inspector/InspectorDetailsSpec.js12
-rw-r--r--src/ui/inspector/details/Properties.vue36
7 files changed, 95 insertions, 17 deletions
diff --git a/src/api/annotation/AnnotationAPISpec.js b/src/api/annotation/AnnotationAPISpec.js
index a7e2a162d..154369a84 100644
--- a/src/api/annotation/AnnotationAPISpec.js
+++ b/src/api/annotation/AnnotationAPISpec.js
@@ -94,7 +94,6 @@ describe("The Annotation API", () => {
openmct.startHeadless();
});
afterEach(async () => {
- openmct.objects.providers = {};
await resetApplicationState(openmct);
});
it("is defined", () => {
diff --git a/src/api/objects/ObjectAPI.js b/src/api/objects/ObjectAPI.js
index 1d0ccfe25..218f1018e 100644
--- a/src/api/objects/ObjectAPI.js
+++ b/src/api/objects/ObjectAPI.js
@@ -96,7 +96,7 @@ export default class ObjectAPI {
this.cache = {};
this.interceptorRegistry = new InterceptorRegistry();
- this.SYNCHRONIZED_OBJECT_TYPES = ['notebook', 'plan', 'annotation'];
+ this.SYNCHRONIZED_OBJECT_TYPES = ['notebook', 'restricted-notebook', 'plan', 'annotation'];
this.errors = {
Conflict: ConflictError
@@ -354,7 +354,7 @@ 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
*/
- save(domainObject) {
+ async save(domainObject) {
let provider = this.getProvider(domainObject.identifier);
let savedResolve;
let savedReject;
@@ -372,6 +372,8 @@ export default class ObjectAPI {
savedReject = reject;
});
domainObject.persisted = persistedTime;
+ domainObject.created = persistedTime;
+ domainObject.createdBy = await this.#getCurrentUsername();
const newObjectPromise = provider.create(domainObject);
if (newObjectPromise) {
newObjectPromise.then(response => {
@@ -385,6 +387,7 @@ export default class ObjectAPI {
}
} else {
domainObject.persisted = persistedTime;
+ domainObject.modifiedBy = await this.#getCurrentUsername();
this.mutate(domainObject, 'persisted', persistedTime);
result = provider.update(domainObject);
}
@@ -399,6 +402,17 @@ export default class ObjectAPI {
});
}
+ async #getCurrentUsername() {
+ const user = await this.openmct.user.getCurrentUser();
+ let username;
+
+ if (user !== undefined) {
+ username = user.getName();
+ }
+
+ return username;
+ }
+
/**
* After entering into edit mode, creates a new instance of Transaction to keep track of changes in Objects
*/
diff --git a/src/api/objects/ObjectAPISpec.js b/src/api/objects/ObjectAPISpec.js
index 950b356be..344c67d10 100644
--- a/src/api/objects/ObjectAPISpec.js
+++ b/src/api/objects/ObjectAPISpec.js
@@ -8,13 +8,27 @@ describe("The Object API", () => {
let mockDomainObject;
const TEST_NAMESPACE = "test-namespace";
const TEST_KEY = "test-key";
+ const USERNAME = 'Joan Q Public';
const FIFTEEN_MINUTES = 15 * 60 * 1000;
beforeEach((done) => {
typeRegistry = jasmine.createSpyObj('typeRegistry', [
'get'
]);
+ const userProvider = {
+ isLoggedIn() {
+ return true;
+ },
+ getCurrentUser() {
+ return Promise.resolve({
+ getName() {
+ return USERNAME;
+ }
+ });
+ }
+ };
openmct = createOpenMct();
+ openmct.user.setProvider(userProvider);
objectAPI = openmct.objects;
openmct.editor = {};
@@ -63,19 +77,34 @@ describe("The Object API", () => {
mockProvider.update.and.returnValue(Promise.resolve(true));
objectAPI.addProvider(TEST_NAMESPACE, mockProvider);
});
- it("Calls 'create' on provider if object is new", () => {
+ it("Adds a 'created' timestamp to new objects", () => {
objectAPI.save(mockDomainObject);
+ expect(mockDomainObject.created).not.toBeUndefined();
+ });
+ it("Calls 'create' on provider if object is new", async () => {
+ await objectAPI.save(mockDomainObject);
expect(mockProvider.create).toHaveBeenCalled();
expect(mockProvider.update).not.toHaveBeenCalled();
});
- it("Calls 'update' on provider if object is not new", () => {
+ it("Calls 'update' on provider if object is not new", async () => {
mockDomainObject.persisted = Date.now() - FIFTEEN_MINUTES;
mockDomainObject.modified = Date.now();
- objectAPI.save(mockDomainObject);
+ await objectAPI.save(mockDomainObject);
expect(mockProvider.create).not.toHaveBeenCalled();
expect(mockProvider.update).toHaveBeenCalled();
});
+ it("Sets the current user for 'createdBy' on new objects", async () => {
+ await objectAPI.save(mockDomainObject);
+ expect(mockDomainObject.createdBy).toBe(USERNAME);
+ });
+ it("Sets the current user for 'modifedBy' on existing objects", async () => {
+ mockDomainObject.persisted = Date.now() - FIFTEEN_MINUTES;
+ mockDomainObject.modified = Date.now();
+
+ await objectAPI.save(mockDomainObject);
+ expect(mockDomainObject.modifiedBy).toBe(USERNAME);
+ });
it("Does not persist if the object is unchanged", () => {
mockDomainObject.persisted =
diff --git a/src/plugins/plan/pluginSpec.js b/src/plugins/plan/pluginSpec.js
index d981ac265..781ae7710 100644
--- a/src/plugins/plan/pluginSpec.js
+++ b/src/plugins/plan/pluginSpec.js
@@ -264,7 +264,7 @@ describe('the plugin', function () {
it('provides an inspector view with the version information if available', () => {
componentObject = component.$root.$children[0];
const propertiesEls = componentObject.$el.querySelectorAll('.c-inspect-properties__row');
- expect(propertiesEls.length).toEqual(4);
+ expect(propertiesEls.length).toEqual(6);
const found = Array.from(propertiesEls).some((propertyEl) => {
return (propertyEl.children[0].innerHTML.trim() === 'Version'
&& propertyEl.children[1].innerHTML.trim() === 'v1');
diff --git a/src/plugins/remove/RemoveAction.js b/src/plugins/remove/RemoveAction.js
index 8a0b9ec4c..c037f9984 100644
--- a/src/plugins/remove/RemoveAction.js
+++ b/src/plugins/remove/RemoveAction.js
@@ -34,8 +34,8 @@ export default class RemoveAction {
invoke(objectPath) {
let object = objectPath[0];
let parent = objectPath[1];
- this.showConfirmDialog(object).then(() => {
- this.removeFromComposition(parent, object);
+ this.showConfirmDialog(object).then(async () => {
+ await this.removeFromComposition(parent, object);
if (this.inNavigationPath(object)) {
this.navigateTo(objectPath.slice(1));
}
@@ -81,7 +81,7 @@ export default class RemoveAction {
this.openmct.router.navigate('#/browse/' + urlPath);
}
- removeFromComposition(parent, child) {
+ async removeFromComposition(parent, child) {
let composition = parent.composition.filter(id =>
!this.openmct.objects.areIdsEqual(id, child.identifier)
);
@@ -93,7 +93,7 @@ export default class RemoveAction {
}
if (!this.isAlias(child, parent)) {
- this.openmct.objects.mutate(child, 'location', null);
+ await this.openmct.objects.mutate(child, 'location', null);
}
}
diff --git a/src/ui/inspector/InspectorDetailsSpec.js b/src/ui/inspector/InspectorDetailsSpec.js
index 24553c5c4..208069fb0 100644
--- a/src/ui/inspector/InspectorDetailsSpec.js
+++ b/src/ui/inspector/InspectorDetailsSpec.js
@@ -38,6 +38,8 @@ describe('the inspector', () => {
folderItem = {
name: 'folder',
type: 'folder',
+ createdBy: 'John Q',
+ modifiedBy: 'Public',
id: 'mock-folder-key',
identifier: {
namespace: '',
@@ -74,6 +76,8 @@ describe('the inspector', () => {
const [
title,
type,
+ createdBy,
+ modifiedBy,
notes,
timestamp
] = details;
@@ -87,6 +91,14 @@ describe('the inspector', () => {
.toEqual('Type');
expect(type.value.toLowerCase())
.toEqual(folderItem.type);
+ expect(createdBy.name)
+ .toEqual('Created By');
+ expect(createdBy.value)
+ .toEqual(folderItem.createdBy);
+ expect(modifiedBy.name)
+ .toEqual('Modified By');
+ expect(modifiedBy.value)
+ .toEqual(folderItem.modifiedBy);
expect(notes.value)
.toEqual('This object should have some notes');
diff --git a/src/ui/inspector/details/Properties.vue b/src/ui/inspector/details/Properties.vue
index fe0edda9e..d14a2b4ed 100644
--- a/src/ui/inspector/details/Properties.vue
+++ b/src/ui/inspector/details/Properties.vue
@@ -90,10 +90,13 @@ export default {
return;
}
+ const UNKNOWN_USER = 'Unknown';
const title = this.domainObject.name;
const typeName = this.type ? this.type.definition.name : `Unknown: ${this.domainObject.type}`;
- const timestampLabel = this.domainObject.modified ? 'Modified' : 'Created';
- const timestamp = this.domainObject.modified ? this.domainObject.modified : this.domainObject.created;
+ const createdTimestamp = this.domainObject.created;
+ const createdBy = this.domainObject.createdBy ? this.domainObject.createdBy : UNKNOWN_USER;
+ const modifiedBy = this.domainObject.modifiedBy ? this.domainObject.modifiedBy : UNKNOWN_USER;
+ const modifiedTimestamp = this.domainObject.modified ? this.domainObject.modified : this.domainObject.created;
const notes = this.domainObject.notes;
const version = this.domainObject.version;
@@ -105,6 +108,14 @@ export default {
{
name: 'Type',
value: typeName
+ },
+ {
+ name: 'Created By',
+ value: createdBy
+ },
+ {
+ name: 'Modified By',
+ value: modifiedBy
}
];
@@ -115,15 +126,28 @@ export default {
});
}
- if (timestamp !== undefined) {
- const formattedTimestamp = Moment.utc(timestamp)
+ if (createdTimestamp !== undefined) {
+ const formattedCreatedTimestamp = Moment.utc(createdTimestamp)
+ .format('YYYY-MM-DD[\n]HH:mm:ss')
+ + ' UTC';
+
+ details.push(
+ {
+ name: 'Created',
+ value: formattedCreatedTimestamp
+ }
+ );
+ }
+
+ if (modifiedTimestamp !== undefined) {
+ const formattedModifiedTimestamp = Moment.utc(modifiedTimestamp)
.format('YYYY-MM-DD[\n]HH:mm:ss')
+ ' UTC';
details.push(
{
- name: timestampLabel,
- value: formattedTimestamp
+ name: 'Modified',
+ value: formattedModifiedTimestamp
}
);
}