Compare commits

...

45 Commits

Author SHA1 Message Date
Michael Rogers
15becc9916 Add role attribution to notebook entries and export 2023-07-12 09:23:02 -05:00
John Hill
718ce19afe [CI] Update Github Actions to combine deploysentinel PR reports and driveby (#6784)
* include git hash

* skip a test
2023-07-12 09:23:02 -05:00
Michael Rogers
f7bc4975b3 Dismiss overlay from exampleUser plugin which affected menu api positioning 2023-07-11 15:38:28 -05:00
Michael Rogers
1d2c56eabf example-role to flight 2023-07-10 15:42:59 -05:00
Michael Rogers
3dce8a4133 Add role selection step to beforeEach 2023-07-10 15:32:45 -05:00
Michael Rogers
6c004d265b iconClass to alert 2023-07-10 14:34:03 -05:00
Scott Bell
a148f102e1 Merge branch 'master' into mct6555 2023-07-10 10:11:05 +02:00
Michael Rogers
a7e10a9cd5 Change RoleChannel to ActiveRoleSynchronizer and update method calls to match 2023-07-07 15:08:22 -05:00
Michael Rogers
5a6755c3fb Throw error on no provider 2023-07-06 15:25:40 -05:00
Michael Rogers
06f71412a6 RoleChannelProvider to RoleChannel 2023-07-06 15:15:33 -05:00
Michael Rogers
aba026ffd2 Copyright statement 2023-07-06 15:12:52 -05:00
Michael Rogers
be5ba7c69d Merge conflict extra line 2023-06-23 16:39:07 -05:00
Michael Rogers
b0b92eeac6 Undid merge error 2023-06-23 16:24:17 -05:00
Michael Rogers
de07e0326c Merge branch 'master' into mct6555 2023-06-23 16:18:22 -05:00
Michael Rogers
f51d47c0e4 refactor: format with prettier 2023-06-23 15:52:03 -05:00
Michael Rogers
e53e761a1d Remove unneccesary filter on allRoles 2023-06-23 15:47:30 -05:00
Michael Rogers
4902b31270 Return null instead of undefined 2023-06-23 15:47:30 -05:00
Michael Rogers
295efee47a console.log 2023-06-23 15:47:30 -05:00
Michael Rogers
97ae44b5af Update OperatorStatusIndicator install to hide if an observer 2023-06-23 15:47:30 -05:00
Michael Rogers
6c087a1d1e Update default selection for roles if input is not chosen 2023-06-23 15:47:30 -05:00
Michael Rogers
76faabf471 Update statusRole parameter name 2023-06-23 15:47:30 -05:00
Michael Rogers
71d8ffdd80 More flight like status role names and parameter names 2023-06-23 15:47:30 -05:00
Michael Rogers
528f87e930 Separated input value from active role value 2023-06-23 15:47:30 -05:00
Michael Rogers
6444e9139e Moved roleChannel to private field 2023-06-23 15:47:30 -05:00
Michael Rogers
af1fc68766 Cleanup 2023-06-23 15:47:30 -05:00
Michael Rogers
7388b488f1 Cleanup 2023-06-23 15:47:30 -05:00
Michael Rogers
56827ad17b Removed status.getStatusRoleForCurrentUser 2023-06-23 15:47:30 -05:00
Michael Rogers
c52ee6c40d Remove default status role from example user plugin 2023-06-23 15:47:30 -05:00
Michael Rogers
7a4d661e48 Removed unused role param from status api call 2023-06-23 15:47:30 -05:00
Michael Rogers
7261662975 Lint 2023-06-23 15:47:30 -05:00
Michael Rogers
ec7f954411 Added success notification and cleanup 2023-06-23 15:47:30 -05:00
Michael Rogers
c0e7804ccd Store status roles in an array instead of a singular value 2023-06-23 15:47:30 -05:00
Michael Rogers
550fa7e7e7 Cleanup 2023-06-23 15:47:30 -05:00
Michael Rogers
78d686607e Updates to status api canPRovideStatusForRole 2023-06-23 15:47:30 -05:00
Michael Rogers
20101911d3 Reconnect channel on error and UserIndicator updates 2023-06-23 15:47:30 -05:00
Michael Rogers
1f9cc05ef2 Moved prompt to UserIndicator 2023-06-23 15:47:29 -05:00
Michael Rogers
035cb1e33b UserAPI role updates and UserIndicator improvement 2023-06-23 15:47:29 -05:00
Michael Rogers
01131c1d90 Comment width 2023-06-23 15:47:29 -05:00
Michael Rogers
3b3be58f41 Update comment 2023-06-23 15:47:29 -05:00
Michael Rogers
2c4b3c38fd Updates to broadcast channel lifecycle 2023-06-23 15:47:29 -05:00
Michael Rogers
53fea64152 Display role in user indicator 2023-06-23 15:47:29 -05:00
Michael Rogers
bd561194d5 Added selection dialog to overlays and implemented for operator status 2023-06-23 15:47:29 -05:00
Michael Rogers
6764ff0b33 Update example user provider 2023-06-23 15:47:29 -05:00
Michael Rogers
6a9b9613e4 Add session storage and role to user indicator 2023-06-23 15:47:29 -05:00
Michael Rogers
f5d57210ae Add additional test roles to example user 2023-06-23 15:47:29 -05:00
22 changed files with 450 additions and 76 deletions

View File

@@ -49,6 +49,7 @@ jobs:
- name: Run CouchDB Tests and publish to deploysentinel
env:
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
COMMIT_INFO_SHA: ${{github.event.pull_request.head.sha }}
run: npm run test:e2e:couchdb
- name: Publish Results to Codecov.io

View File

@@ -29,7 +29,8 @@ relates to how we've extended it (i.e. ./e2e/baseFixtures.js) and assumptions ma
const { test } = require('../../baseFixtures.js');
test.describe('baseFixtures tests', () => {
test('Verify that tests fail if console.error is thrown', async ({ page }) => {
//Skip this test for now https://github.com/nasa/openmct/issues/6785
test.fixme('Verify that tests fail if console.error is thrown', async ({ page }) => {
test.fail();
//Go to baseURL
await page.goto('./', { waitUntil: 'domcontentloaded' });

View File

@@ -47,6 +47,11 @@ test.describe('Operator Status', () => {
path: path.join(__dirname, '../../../../helper/', 'addInitOperatorStatus.js')
});
await page.goto('./', { waitUntil: 'domcontentloaded' });
await expect(page.getByText('Select Role')).toBeVisible();
// set role
await page.getByRole('button', { name: 'Select' }).click();
// dismiss role confirmation popup
await page.getByRole('button', { name: 'Dismiss' }).click();
});
// verify that operator status is visible

View File

@@ -63,16 +63,24 @@ const STATUSES = [
* @implements {StatusUserProvider}
*/
export default class ExampleUserProvider extends EventEmitter {
constructor(openmct, { defaultStatusRole } = { defaultStatusRole: undefined }) {
constructor(
openmct,
{ statusRoles } = {
statusRoles: []
}
) {
super();
this.openmct = openmct;
this.user = undefined;
this.loggedIn = false;
this.autoLoginUser = undefined;
this.status = STATUSES[0];
this.statusRoleValues = statusRoles.map((role) => ({
role: role,
status: STATUSES[0]
}));
this.pollQuestion = undefined;
this.defaultStatusRole = defaultStatusRole;
this.statusRoles = statusRoles;
this.ExampleUser = createExampleUser(this.openmct.user.User);
this.loginPromise = undefined;
@@ -94,14 +102,13 @@ export default class ExampleUserProvider extends EventEmitter {
return this.loginPromise;
}
canProvideStatusForRole() {
return Promise.resolve(true);
canProvideStatusForRole(role) {
return this.statusRoles.includes(role);
}
canSetPollQuestion() {
return Promise.resolve(true);
}
hasRole(roleId) {
if (!this.loggedIn) {
Promise.resolve(undefined);
@@ -110,16 +117,24 @@ export default class ExampleUserProvider extends EventEmitter {
return Promise.resolve(this.user.getRoles().includes(roleId));
}
getStatusRoleForCurrentUser() {
return Promise.resolve(this.defaultStatusRole);
getPossibleRoles() {
return this.user.getRoles();
}
getStatusRoleForCurrentUser(role) {
const matchedRole = this.statusRoleValues.find((statusRole) => statusRole.role === role);
return Promise.resolve(matchedRole?.status);
}
getAllStatusRoles() {
return Promise.resolve([this.defaultStatusRole]);
return Promise.resolve(this.statusRoles);
}
getStatusForRole(role) {
return Promise.resolve(this.status);
const statusForRole = this.statusRoleValues.find((statusRole) => statusRole.role === role);
return Promise.resolve(statusForRole?.status);
}
async getDefaultStatusForRole(role) {
@@ -130,7 +145,8 @@ export default class ExampleUserProvider extends EventEmitter {
setStatusForRole(role, status) {
status.timestamp = Date.now();
this.status = status;
const matchingIndex = this.statusRoleValues.findIndex((statusRole) => statusRole.role === role);
this.statusRoleValues[matchingIndex].status = status;
this.emit('statusChange', {
role,
status
@@ -175,7 +191,7 @@ export default class ExampleUserProvider extends EventEmitter {
// for testing purposes, this will skip the form, this wouldn't be used in
// a normal authentication process
if (this.autoLoginUser) {
this.user = new this.ExampleUser(id, this.autoLoginUser, ['example-role']);
this.user = new this.ExampleUser(id, this.autoLoginUser, ['flight', 'driver', 'observer']);
this.loggedIn = true;
return Promise.resolve();

View File

@@ -21,16 +21,18 @@
*****************************************************************************/
import ExampleUserProvider from './ExampleUserProvider';
const AUTO_LOGIN_USER = 'mct-user';
const STATUS_ROLES = ['flight', 'driver'];
export default function ExampleUserPlugin(
{ autoLoginUser, defaultStatusRole } = {
autoLoginUser: 'guest',
defaultStatusRole: 'test-role'
{ autoLoginUser, statusRoles } = {
autoLoginUser: AUTO_LOGIN_USER,
statusRoles: STATUS_ROLES
}
) {
return function install(openmct) {
const userProvider = new ExampleUserProvider(openmct, {
defaultStatusRole
statusRoles
});
if (autoLoginUser !== undefined) {

View File

@@ -31,6 +31,8 @@ describe('The Example User Plugin', () => {
});
afterEach(() => {
const activeOverlays = openmct.overlays.activeOverlays;
activeOverlays.forEach((overlay) => overlay.dismiss());
return resetApplicationState(openmct);
});

View File

@@ -1,6 +1,29 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import Overlay from './Overlay';
import Dialog from './Dialog';
import ProgressDialog from './ProgressDialog';
import Selection from './Selection';
/**
* The OverlayAPI is responsible for pre-pending templates to
@@ -130,6 +153,13 @@ class OverlayAPI {
return progressDialog;
}
selection(options) {
let selection = new Selection(options);
this.showOverlay(selection);
return selection;
}
}
export default OverlayAPI;

View File

@@ -0,0 +1,67 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import SelectionComponent from './components/SelectionComponent.vue';
import Overlay from './Overlay';
import Vue from 'vue';
class Selection extends Overlay {
constructor({
iconClass,
title,
message,
selectionOptions,
onChange,
currentSelection,
...options
}) {
let component = new Vue({
components: {
SelectionComponent: SelectionComponent
},
provide: {
iconClass,
title,
message,
selectionOptions,
onChange,
currentSelection
},
template: '<selection-component></selection-component>'
}).$mount();
super({
element: component.$el,
size: 'fit',
dismissable: false,
onChange,
currentSelection,
...options
});
this.once('destroy', () => {
component.$destroy();
});
}
}
export default Selection;

View File

@@ -0,0 +1,34 @@
<template>
<div class="c-message">
<!--Uses flex-row -->
<div class="c-message__icon" :class="['u-icon-bg-color-' + iconClass]"></div>
<div class="c-message__text">
<!-- Uses flex-column -->
<div v-if="title" class="c-message__title">
{{ title }}
</div>
<div v-if="message" class="c-message__action-text">
{{ message }}
</div>
<select @change="onChange">
<option
v-for="option in selectionOptions"
:key="option.key"
:value="option.key"
:selected="option.key === currentSelection"
>
{{ option.name }}
</option>
</select>
<slot></slot>
</div>
</div>
</template>
<script>
export default {
inject: ['iconClass', 'title', 'message', 'selectionOptions', 'currentSelection', 'onChange']
};
</script>

View File

@@ -0,0 +1,41 @@
import { ACTIVE_ROLE_BROADCAST_CHANNEL_NAME } from './constants';
class ActiveRoleSynchronizer {
#roleChannel;
constructor(openmct) {
this.openmct = openmct;
this.#roleChannel = new BroadcastChannel(ACTIVE_ROLE_BROADCAST_CHANNEL_NAME);
this.setActiveRoleFromChannelMessage = this.setActiveRoleFromChannelMessage.bind(this);
this.subscribeToRoleChanges(this.setActiveRoleFromChannelMessage);
}
extractRoleFromEvent(callback) {
return function (event) {
callback(event.data);
};
}
subscribeToRoleChanges(callback) {
this.#roleChannel.addEventListener('message', this.extractRoleFromEvent(callback));
}
unsubscribeFromRoleChanges(callback) {
this.#roleChannel.removeEventListener('message', this.extractRoleFromEvent(callback));
}
setActiveRoleFromChannelMessage(role) {
this.openmct.user.setActiveRole(role);
}
broadcastNewRole(role) {
if (!this.#roleChannel.name) {
return false;
}
this.#roleChannel.postMessage(role);
}
destroy() {
this.unsubscribeFromRoleChanges(this.setActiveRoleFromChannelMessage);
this.#roleChannel.close();
}
}
export default ActiveRoleSynchronizer;

View File

@@ -0,0 +1,37 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import { SESSION_STORAGE_KEY } from './constants';
class SessionPersistance {
getActiveRole() {
return sessionStorage.getItem(SESSION_STORAGE_KEY);
}
setActiveRole(role) {
return sessionStorage.setItem(SESSION_STORAGE_KEY, role);
}
clearActiveRole() {
return sessionStorage.removeItem(SESSION_STORAGE_KEY);
}
}
export default new SessionPersistance();

View File

@@ -140,9 +140,9 @@ export default class StatusAPI extends EventEmitter {
const provider = this.#userAPI.getProvider();
if (provider.canProvideStatusForRole) {
return provider.canProvideStatusForRole(role);
return Promise.resolve(provider.canProvideStatusForRole(role));
} else {
return false;
return Promise.resolve(false);
}
}
@@ -151,11 +151,16 @@ export default class StatusAPI extends EventEmitter {
* @param {Status} status The status to set for the provided role
* @returns {Promise<Boolean>} true if operation was successful, otherwise false.
*/
setStatusForRole(role, status) {
setStatusForRole(status) {
const provider = this.#userAPI.getProvider();
if (provider.setStatusForRole) {
return provider.setStatusForRole(role, status);
const activeRole = this.#userAPI.getActiveRole();
if (!provider.canProvideStatusForRole(activeRole)) {
return false;
}
return provider.setStatusForRole(activeRole, status);
} else {
this.#userAPI.error('User provider does not support setting role status');
}
@@ -216,21 +221,6 @@ export default class StatusAPI extends EventEmitter {
}
}
/**
* The status role of the current user. A user may have multiple roles, but will only have one role
* that provides status at any time.
* @returns {Promise<import("./UserAPI").Role>} the role for which the current user can provide status.
*/
getStatusRoleForCurrentUser() {
const provider = this.#userAPI.getProvider();
if (provider.getStatusRoleForCurrentUser) {
return provider.getStatusRoleForCurrentUser();
} else {
this.#userAPI.error('User provider cannot provide role status for this user');
}
}
/**
* @returns {Promise<Boolean>} true if the configured UserProvider can provide status for the currently logged in user, false otherwise.
* @see StatusUserProvider
@@ -238,14 +228,13 @@ export default class StatusAPI extends EventEmitter {
async canProvideStatusForCurrentUser() {
const provider = this.#userAPI.getProvider();
if (provider.getStatusRoleForCurrentUser) {
const activeStatusRole = await this.#userAPI.getProvider().getStatusRoleForCurrentUser();
const canProvideStatus = await this.canProvideStatusForRole(activeStatusRole);
return canProvideStatus;
} else {
if (!provider) {
return false;
}
const activeStatusRole = await this.#userAPI.getActiveRole();
const canProvideStatus = await this.canProvideStatusForRole(activeStatusRole);
return canProvideStatus;
}
/**

View File

@@ -77,5 +77,4 @@ export default class StatusUserProvider extends UserProvider {
/**
* @returns {Promise<import("./UserAPI").Role>} the active status role for the currently logged in user
*/
async getStatusRoleForCurrentUser() {}
}

View File

@@ -24,6 +24,7 @@ import EventEmitter from 'EventEmitter';
import { MULTIPLE_PROVIDER_ERROR, NO_PROVIDER_ERROR } from './constants';
import StatusAPI from './StatusAPI';
import User from './User';
import SessionPersistance from './SessionPersistance';
class UserAPI extends EventEmitter {
/**
@@ -86,6 +87,58 @@ class UserAPI extends EventEmitter {
return this._provider.getCurrentUser();
}
}
/**
* If a user provider is set, it will return an array of possible roles
* that can be selected by the current user
* @memberof module:openmct.UserAPI#
* @returns {Array}
* @throws Will throw an error if no user provider is set
*/
getPossibleRoles() {
if (!this.hasProvider()) {
this.error(NO_PROVIDER_ERROR);
}
return this._provider.getPossibleRoles();
}
/**
* If a user provider is set, it will return the active role or null
* @memberof module:openmct.UserAPI#
* @returns {string|null}
*/
getActiveRole() {
if (!this.hasProvider()) {
return null;
}
// get from session storage
const sessionStorageValue = SessionPersistance.getActiveRole();
return sessionStorageValue;
}
/**
* Set the active role in session storage
* @memberof module:openmct.UserAPI#
* @returns {undefined}
*/
setActiveRole(role) {
SessionPersistance.setActiveRole(role);
this.emit('roleChanged', role);
}
/**
* Will return if a role can provide a operator status response
* @memberof module:openmct.UserApi#
* @returns {Boolean}
*/
canProvideStatusForRole() {
if (!this.hasProvider()) {
return null;
}
const activeRole = this.getActiveRole();
return this._provider.canProvideStatusForRole?.(activeRole);
}
/**
* If a user provider is set, it will return the user provider's

View File

@@ -25,7 +25,7 @@ import { MULTIPLE_PROVIDER_ERROR } from './constants';
import ExampleUserProvider from '../../../example/exampleUser/ExampleUserProvider';
const USERNAME = 'Test User';
const EXAMPLE_ROLE = 'example-role';
const EXAMPLE_ROLE = 'flight';
describe('The User API', () => {
let openmct;

View File

@@ -22,3 +22,6 @@
export const MULTIPLE_PROVIDER_ERROR = 'Only one user provider may be set at a time.';
export const NO_PROVIDER_ERROR = 'No user provider has been set.';
export const SESSION_STORAGE_KEY = 'ACTIVE_USER_ROLE';
export const ACTIVE_ROLE_BROADCAST_CHANNEL_NAME = 'ActiveRoleChannel';

View File

@@ -105,14 +105,16 @@ export default class ExportNotebookAsTextAction {
if (changes.exportMetaData) {
const createdTimestamp = entry.createdOn;
const createdBy = this.getUserName(entry.createdBy);
const createdByRole = entry.createdByRole;
const modifiedBy = this.getUserName(entry.modifiedBy);
const modifiedByRole = entry.modifiedByRole;
const modifiedTimestamp = entry.modified ?? entry.created;
notebookAsText += `Created on ${this.formatTimeStamp(
createdTimestamp
)} by user ${createdBy}\n\n`;
)} by user ${createdBy}${createdByRole ? `: ${createdByRole}` : ''}\n\n`;
notebookAsText += `Updated on ${this.formatTimeStamp(
modifiedTimestamp
)} by user ${modifiedBy}\n\n`;
)} by user ${modifiedBy}${modifiedByRole ? `: ${modifiedByRole}` : ''}\n\n`;
}
if (changes.exportTags) {

View File

@@ -37,7 +37,10 @@
<span class="c-ne__created-date">{{ createdOnDate }}</span>
<span class="c-ne__created-time">{{ createdOnTime }}</span>
<span v-if="entry.createdBy" class="c-ne__creator">
<span class="icon-person"></span> {{ entry.createdBy }}
<span class="icon-person"></span>
{{
entry.createdByRole ? `${entry.createdBy}: ${entry.createdByRole}` : entry.createdBy
}}
</span>
</div>
<span v-if="!readOnly && !isLocked" class="c-ne__local-controls--hidden">
@@ -433,10 +436,17 @@ export default {
this.timestampAndUpdate();
},
async timestampAndUpdate() {
const user = await this.openmct.user.getCurrentUser();
const [user, activeRole] = await Promise.all([
this.openmct.user.getCurrentUser(),
this.openmct.user.getActiveRole?.()
]);
if (user === undefined) {
this.entry.modifiedBy = UNKNOWN_USER;
} else {
this.entry.modifiedBy = user.getName();
if (activeRole) {
this.entry.modifiedByRole = activeRole;
}
}
this.entry.modified = Date.now();

View File

@@ -12,6 +12,15 @@ async function getUsername(openmct) {
return username;
}
async function getActiveRole(openmct) {
let role = null;
if (openmct.user.hasProvider()) {
role = await openmct.user.getActiveRole?.();
}
return role;
}
export const DEFAULT_CLASS = 'notebook-default';
const TIME_BOUNDS = {
START_BOUND: 'tc.startBound',
@@ -156,11 +165,15 @@ export async function addNotebookEntry(
const embeds = embed ? [embed] : [];
const id = `entry-${uuid()}`;
const createdBy = await getUsername(openmct);
const [createdBy, createdByRole] = await Promise.all([
getUsername(openmct),
getActiveRole(openmct)
]);
const entry = {
id,
createdOn: date,
createdBy,
createdByRole,
text: entryText,
embeds
};

View File

@@ -48,7 +48,6 @@
<script>
const DEFAULT_POLL_QUESTION = 'NO POLL QUESTION';
export default {
inject: ['openmct', 'indicator', 'configuration'],
props: {
@@ -63,7 +62,6 @@ export default {
},
data() {
return {
allRoles: [],
role: '--',
pollQuestionUpdated: '--',
currentPollQuestion: DEFAULT_POLL_QUESTION,
@@ -78,26 +76,27 @@ export default {
left: `${this.positionX}px`,
top: `${this.positionY}px`
};
},
canProvideStatusForRole() {
return this.openmct.user.canProvideStatusForRole(this.role);
}
},
beforeDestroy() {
this.openmct.user.status.off('statusChange', this.setStatus);
this.openmct.user.status.off('pollQuestionChange', this.setPollQuestion);
this.openmct.user.off('roleChanged', this.fetchMyStatus);
},
async mounted() {
this.unsubscribe = [];
await this.fetchUser();
await this.findFirstApplicableRole();
this.fetchPossibleStatusesForUser();
this.fetchCurrentPoll();
this.fetchMyStatus();
await this.fetchMyStatus();
this.subscribeToMyStatus();
this.subscribeToPollQuestion();
this.subscribeToRoleChange();
},
methods: {
async findFirstApplicableRole() {
this.role = await this.openmct.user.status.getStatusRoleForCurrentUser();
},
async fetchUser() {
this.user = await this.openmct.user.getCurrentUser();
},
@@ -117,9 +116,17 @@ export default {
this.indicator.text(pollQuestion?.question || '');
},
async fetchMyStatus() {
const activeStatusRole = await this.openmct.user.status.getStatusRoleForCurrentUser();
const status = await this.openmct.user.status.getStatusForRole(activeStatusRole);
const activeRole = await this.openmct.user.getActiveRole();
this.role = activeRole;
// hide indicator for observer
if (!this.openmct.user.canProvideStatusForRole()) {
this.indicator.text('');
this.indicator.statusClass('hidden');
return;
}
const status = await this.openmct.user.status.getStatusForRole(activeRole);
if (status !== undefined) {
this.setStatus({ status });
}
@@ -130,7 +137,10 @@ export default {
subscribeToPollQuestion() {
this.openmct.user.status.on('pollQuestionChange', this.setPollQuestion);
},
setStatus({ role, status }) {
subscribeToRoleChange() {
this.openmct.user.on('roleChanged', this.fetchMyStatus);
},
setStatus({ status }) {
status = this.applyStyling(status);
this.selectedStatus = status.key;
this.indicator.iconClass(status.iconClassPoll);
@@ -148,11 +158,16 @@ export default {
return this.allStatuses.find((possibleMatch) => possibleMatch.key === statusKey);
},
async changeStatus() {
if (!this.openmct.user.canProvideStatusForRole()) {
this.openmct.notifications.error('Selected role is ineligible to provide operator status');
return;
}
if (this.selectedStatus !== undefined) {
const statusObject = this.findStatusByKey(this.selectedStatus);
const result = await this.openmct.user.status.setStatusForRole(this.role, statusObject);
const result = await this.openmct.user.status.setStatusForRole(statusObject);
if (result === true) {
this.openmct.notifications.info('Successfully set operator status');
} else {

View File

@@ -29,13 +29,8 @@ import PollQuestionIndicator from './pollQuestion/PollQuestionIndicator';
export default function operatorStatusPlugin(configuration) {
return function install(openmct) {
if (openmct.user.hasProvider()) {
openmct.user.status.canProvideStatusForCurrentUser().then((canProvideStatus) => {
if (canProvideStatus) {
const operatorStatusIndicator = new OperatorStatusIndicator(openmct, configuration);
operatorStatusIndicator.install();
}
});
const operatorStatusIndicator = new OperatorStatusIndicator(openmct, configuration);
operatorStatusIndicator.install();
openmct.user.status.canSetPollQuestion().then((canSetPollQuestion) => {
if (canSetPollQuestion) {

View File

@@ -23,30 +23,89 @@
<template>
<div class="c-indicator icon-person c-indicator--clickable">
<span class="label c-indicator__label">
{{ userName }}
{{ role ? `${userName}: ${role}` : userName }}
<button @click="promptForRoleSelection">Change Role</button>
</span>
</div>
</template>
<script>
import ActiveRoleSynchronizer from '../../../api/user/ActiveRoleSynchronizer';
export default {
inject: ['openmct'],
data() {
return {
userName: undefined,
loggedIn: false
role: undefined,
loggedIn: false,
roleChannel: undefined,
inputRoleSelection: undefined
};
},
mounted() {
async mounted() {
this.getUserInfo();
this.roleChannel = new ActiveRoleSynchronizer(this.openmct);
this.roleChannel.subscribeToRoleChanges(this.setRoleSelection);
await this.fetchOrPromptForRole();
},
beforeDestroy() {
this.roleChannel.unsubscribeFromRoleChanges(this.setRoleSelection);
},
methods: {
getUserInfo() {
this.openmct.user.getCurrentUser().then((user) => {
this.userName = user.getName();
this.loggedIn = this.openmct.user.isLoggedIn();
async getUserInfo() {
const user = await this.openmct.user.getCurrentUser();
this.userName = user.getName();
this.role = this.openmct.user.getActiveRole();
this.loggedIn = this.openmct.user.isLoggedIn();
},
fetchOrPromptForRole() {
const UserAPI = this.openmct.user;
const activeRole = UserAPI.getActiveRole();
this.role = activeRole;
if (!activeRole) {
this.promptForRoleSelection();
}
},
promptForRoleSelection() {
const allRoles = this.openmct.user.getPossibleRoles();
const selectionOptions = allRoles.map((role) => ({
key: role,
name: role
}));
const dialog = this.openmct.overlays.selection({
selectionOptions,
iconClass: 'alert',
title: 'Select Role',
message: 'Please select your role for operator status.',
currentSelection: this.role,
onChange: (event) => {
this.inputRoleSelection = event.target.value;
},
buttons: [
{
label: 'Select',
emphasis: true,
callback: () => {
dialog.dismiss();
const inputValueOrDefault = this.inputRoleSelection || selectionOptions[0].key;
this.updateRole(inputValueOrDefault);
this.openmct.notifications.info(`Successfully set new role to ${this.role}`);
}
}
]
});
},
setRoleSelection(role) {
this.role = role;
},
updateRole(role) {
this.setRoleSelection(role);
this.openmct.user.setActiveRole(role);
// update other tabs through broadcast channel
this.roleChannel.broadcastNewRole(role);
}
}
};