Compare commits
45 Commits
master
...
mct6555+mc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15becc9916 | ||
|
|
718ce19afe | ||
|
|
f7bc4975b3 | ||
|
|
1d2c56eabf | ||
|
|
3dce8a4133 | ||
|
|
6c004d265b | ||
|
|
a148f102e1 | ||
|
|
a7e10a9cd5 | ||
|
|
5a6755c3fb | ||
|
|
06f71412a6 | ||
|
|
aba026ffd2 | ||
|
|
be5ba7c69d | ||
|
|
b0b92eeac6 | ||
|
|
de07e0326c | ||
|
|
f51d47c0e4 | ||
|
|
e53e761a1d | ||
|
|
4902b31270 | ||
|
|
295efee47a | ||
|
|
97ae44b5af | ||
|
|
6c087a1d1e | ||
|
|
76faabf471 | ||
|
|
71d8ffdd80 | ||
|
|
528f87e930 | ||
|
|
6444e9139e | ||
|
|
af1fc68766 | ||
|
|
7388b488f1 | ||
|
|
56827ad17b | ||
|
|
c52ee6c40d | ||
|
|
7a4d661e48 | ||
|
|
7261662975 | ||
|
|
ec7f954411 | ||
|
|
c0e7804ccd | ||
|
|
550fa7e7e7 | ||
|
|
78d686607e | ||
|
|
20101911d3 | ||
|
|
1f9cc05ef2 | ||
|
|
035cb1e33b | ||
|
|
01131c1d90 | ||
|
|
3b3be58f41 | ||
|
|
2c4b3c38fd | ||
|
|
53fea64152 | ||
|
|
bd561194d5 | ||
|
|
6764ff0b33 | ||
|
|
6a9b9613e4 | ||
|
|
f5d57210ae |
1
.github/workflows/e2e-couchdb.yml
vendored
1
.github/workflows/e2e-couchdb.yml
vendored
@@ -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
|
||||
|
||||
@@ -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' });
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -31,6 +31,8 @@ describe('The Example User Plugin', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
const activeOverlays = openmct.overlays.activeOverlays;
|
||||
activeOverlays.forEach((overlay) => overlay.dismiss());
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
67
src/api/overlays/Selection.js
Normal file
67
src/api/overlays/Selection.js
Normal 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;
|
||||
34
src/api/overlays/components/SelectionComponent.vue
Normal file
34
src/api/overlays/components/SelectionComponent.vue
Normal 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>
|
||||
41
src/api/user/ActiveRoleSynchronizer.js
Normal file
41
src/api/user/ActiveRoleSynchronizer.js
Normal 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;
|
||||
37
src/api/user/SessionPersistance.js
Normal file
37
src/api/user/SessionPersistance.js
Normal 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();
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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() {}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user