Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Mission Status for Situational Awareness #7418

Merged
merged 34 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
8040d1d
refactor: `UserIndicator` use vue component directly
ozyx Jan 24, 2024
5984315
style(WIP): filler styles for user-indicator
ozyx Jan 24, 2024
0d1a6f9
feat(WIP): working on mission status indicators
ozyx Jan 24, 2024
ebc67eb
feat: support mission statuses
ozyx Jan 24, 2024
f11e4aa
feat(WIP): can display mission statuses now
ozyx Jan 25, 2024
458b282
feat(WIP): add composables and dynamically calculate popup position
ozyx Jan 26, 2024
c24f310
feat(WIP): dismissible popup, use moar compositionAPI
ozyx Jan 26, 2024
10750f3
Closes #7420
charlesh88 Jan 26, 2024
7097101
feat: set/unset mission status for role
ozyx Jan 29, 2024
efd5466
refactor: rename some functions
ozyx Jan 29, 2024
1c4b611
feat: more renaming, get mission role statuses working
ozyx Jan 29, 2024
eece4d0
refactor: more method renaming
ozyx Jan 29, 2024
d0c9d8b
fix: remove dupe method
ozyx Jan 29, 2024
4047014
feat: hook up event listeners
ozyx Jan 29, 2024
1afd7d0
refactor: convert to CompositionAPI and listen to events
ozyx Jan 29, 2024
398731a
fix: add that back in, woops
ozyx Jan 30, 2024
e489b10
test: fix some existing tests
ozyx Jan 30, 2024
785acf4
lint: words for the word god
ozyx Jan 30, 2024
90d0898
Merge branch 'master' into mission-status-situational-awareness
ozyx Jan 30, 2024
cd79fc4
refactor: rename
ozyx Jan 30, 2024
79ac584
fix: setting mission statuses
ozyx Jan 30, 2024
56e56ea
style: fix go styling
ozyx Jan 30, 2024
40be740
style: add mission status button
ozyx Jan 30, 2024
c4a7b39
refactor: rename `MissionRole` -> `MissionAction`
ozyx Jan 30, 2024
f746366
test: fix most existing tests
ozyx Jan 30, 2024
e720796
test: remove integration tests already covered by e2e
ozyx Jan 31, 2024
b44a5b2
docs: add documentation
ozyx Jan 31, 2024
8b52e67
Merge branch 'master' into mission-status-situational-awareness
ozyx Jan 31, 2024
8215d6e
refactor: rename
ozyx Jan 31, 2024
ec50a74
fix: a comma
ozyx Jan 31, 2024
a72912d
refactor: a word
ozyx Jan 31, 2024
26835c3
fix: emit parameter format
ozyx Feb 1, 2024
ed6e7e8
fix: correct emit for `missionStatusActionChange`
ozyx Feb 2, 2024
8e7d01d
Merge branch 'master' into mission-status-situational-awareness
unlikelyzero Feb 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@
"unnnormalized",
"checksnapshots",
"specced",
"composables",
"countup"
],
"dictionaries": ["npm", "softwareTerms", "node", "html", "css", "bash", "en_US"],
Expand Down
5 changes: 5 additions & 0 deletions example/exampleUser/ExampleUserProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ export default class ExampleUserProvider extends EventEmitter {
canSetPollQuestion() {
return Promise.resolve(true);
}

canSetMissionStatus() {
return Promise.resolve(false);
}

hasRole(roleId) {
if (!this.loggedIn) {
Promise.resolve(undefined);
Expand Down
101 changes: 101 additions & 0 deletions src/api/user/StatusAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

this.onProviderStatusChange = this.onProviderStatusChange.bind(this);
this.onProviderPollQuestionChange = this.onProviderPollQuestionChange.bind(this);
this.onMissionActionStatusChange = this.onMissionActionStatusChange.bind(this);
this.listenToStatusEvents = this.listenToStatusEvents.bind(this);

this.#openmct.once('destroy', () => {
Expand All @@ -40,6 +41,7 @@
if (typeof provider?.off === 'function') {
provider.off('statusChange', this.onProviderStatusChange);
provider.off('pollQuestionChange', this.onProviderPollQuestionChange);
provider.off('missionActionStatusChange', this.onMissionActionStatusChange);
}
});

Expand Down Expand Up @@ -100,6 +102,67 @@
}
}

/**
* Can the currently logged in user set the mission status.
* @returns {Promise<Boolean>} true if the currently logged in user can set the mission status, false otherwise.
*/
canSetMissionStatus() {
const provider = this.#userAPI.getProvider();

if (provider.canSetMissionStatus) {
return provider.canSetMissionStatus();
} else {
return Promise.resolve(false);

Check warning on line 115 in src/api/user/StatusAPI.js

View check run for this annotation

Codecov / codecov/patch

src/api/user/StatusAPI.js#L115

Added line #L115 was not covered by tests
}
}

/**
* Fetch the current status for the given mission action
* @param {MissionAction} action
* @returns {string}
*/
getStatusForMissionAction(action) {
const provider = this.#userAPI.getProvider();

Check warning on line 125 in src/api/user/StatusAPI.js

View check run for this annotation

Codecov / codecov/patch

src/api/user/StatusAPI.js#L125

Added line #L125 was not covered by tests

if (provider.getStatusForMissionAction) {
return provider.getStatusForMissionAction(action);

Check warning on line 128 in src/api/user/StatusAPI.js

View check run for this annotation

Codecov / codecov/patch

src/api/user/StatusAPI.js#L127-L128

Added lines #L127 - L128 were not covered by tests
} else {
this.#userAPI.error('User provider does not support getting mission action status');

Check warning on line 130 in src/api/user/StatusAPI.js

View check run for this annotation

Codecov / codecov/patch

src/api/user/StatusAPI.js#L130

Added line #L130 was not covered by tests
}
}

/**
* Fetch the list of possible mission status options (GO, NO-GO, etc.)
* @returns {Promise<MissionStatusOption[]>} the complete list of possible mission statuses
*/
async getPossibleMissionActionStatuses() {
const provider = this.#userAPI.getProvider();

Check warning on line 139 in src/api/user/StatusAPI.js

View check run for this annotation

Codecov / codecov/patch

src/api/user/StatusAPI.js#L139

Added line #L139 was not covered by tests

if (provider.getPossibleMissionActionStatuses) {
const possibleOptions = await provider.getPossibleMissionActionStatuses();

Check warning on line 142 in src/api/user/StatusAPI.js

View check run for this annotation

Codecov / codecov/patch

src/api/user/StatusAPI.js#L141-L142

Added lines #L141 - L142 were not covered by tests

return possibleOptions;

Check warning on line 144 in src/api/user/StatusAPI.js

View check run for this annotation

Codecov / codecov/patch

src/api/user/StatusAPI.js#L144

Added line #L144 was not covered by tests
} else {
this.#userAPI.error('User provider does not support mission status options');

Check warning on line 146 in src/api/user/StatusAPI.js

View check run for this annotation

Codecov / codecov/patch

src/api/user/StatusAPI.js#L146

Added line #L146 was not covered by tests
}
}

/**
* Fetch the list of possible mission actions
* @returns {Promise<string[]>} the list of possible mission actions
*/
async getPossibleMissionActions() {
const provider = this.#userAPI.getProvider();

Check warning on line 155 in src/api/user/StatusAPI.js

View check run for this annotation

Codecov / codecov/patch

src/api/user/StatusAPI.js#L155

Added line #L155 was not covered by tests

if (provider.getPossibleMissionActions) {
const possibleActions = await provider.getPossibleMissionActions();

Check warning on line 158 in src/api/user/StatusAPI.js

View check run for this annotation

Codecov / codecov/patch

src/api/user/StatusAPI.js#L157-L158

Added lines #L157 - L158 were not covered by tests

return possibleActions;

Check warning on line 160 in src/api/user/StatusAPI.js

View check run for this annotation

Codecov / codecov/patch

src/api/user/StatusAPI.js#L160

Added line #L160 was not covered by tests
} else {
this.#userAPI.error('User provider does not support mission statuses');

Check warning on line 162 in src/api/user/StatusAPI.js

View check run for this annotation

Codecov / codecov/patch

src/api/user/StatusAPI.js#L162

Added line #L162 was not covered by tests
}
}

/**
* @returns {Promise<Array<Status>>} the complete list of possible states that an operator can reply to a poll question with.
*/
Expand Down Expand Up @@ -166,6 +229,21 @@
}
}

/**
* @param {MissionAction} action
* @param {MissionStatusOption} status
* @returns {Promise<Boolean>} true if operation was successful, otherwise false.
*/
setStatusForMissionAction(action, status) {
const provider = this.#userAPI.getProvider();

Check warning on line 238 in src/api/user/StatusAPI.js

View check run for this annotation

Codecov / codecov/patch

src/api/user/StatusAPI.js#L238

Added line #L238 was not covered by tests

if (provider.setStatusForMissionAction) {
return provider.setStatusForMissionAction(action, status);

Check warning on line 241 in src/api/user/StatusAPI.js

View check run for this annotation

Codecov / codecov/patch

src/api/user/StatusAPI.js#L240-L241

Added lines #L240 - L241 were not covered by tests
} else {
this.#userAPI.error('User provider does not support setting mission role status');

Check warning on line 243 in src/api/user/StatusAPI.js

View check run for this annotation

Codecov / codecov/patch

src/api/user/StatusAPI.js#L243

Added line #L243 was not covered by tests
}
}

/**
* Resets the status of the provided role back to its default status.
* @param {import("./UserAPI").Role} role The role to set the status for.
Expand Down Expand Up @@ -245,6 +323,7 @@
if (typeof provider.on === 'function') {
provider.on('statusChange', this.onProviderStatusChange);
provider.on('pollQuestionChange', this.onProviderPollQuestionChange);
provider.on('missionActionStatusChange', this.onMissionActionStatusChange);
}
}

Expand All @@ -261,21 +340,43 @@
onProviderPollQuestionChange(pollQuestion) {
this.emit('pollQuestionChange', pollQuestion);
}

/**
* @private
*/
onMissionActionStatusChange({ action, newStatus }) {
this.emit('missionActionStatusChange', action, newStatus);

Check warning on line 348 in src/api/user/StatusAPI.js

View check run for this annotation

Codecov / codecov/patch

src/api/user/StatusAPI.js#L348

Added line #L348 was not covered by tests
}
}

/**
* @typedef {import('./UserProvider')} UserProvider
*/

/**
* @typedef {import('./StatusUserProvider')} StatusUserProvider
*/

/**
* The PollQuestion type
* @typedef {Object} PollQuestion
* @property {String} question - The question to be presented to users
* @property {Number} timestamp - The time that the poll question was set.
*/

/**
* The MissionStatus type
* @typedef {Object} MissionStatusOption
* @extends {Status}
* @property {String} color A color to be used when displaying the mission status
*/

/**
* @typedef {Object} MissionAction
* @property {String} key A unique identifier for this action
* @property {String} label A human readable label for this action
*/

/**
* The Status type
* @typedef {Object} Status
Expand Down
4 changes: 2 additions & 2 deletions src/api/user/StatusUserProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ import UserProvider from './UserProvider.js';

export default class StatusUserProvider extends UserProvider {
/**
* @param {('statusChange'|'pollQuestionChange')} event the name of the event to listen to
* @param {('statusChange'|'pollQuestionChange'|'missionActionStatusChange')} event the name of the event to listen to
* @param {Function} callback a function to invoke when this event occurs
*/
on(event, callback) {}
/**
* @param {('statusChange'|'pollQuestionChange')} event the name of the event to stop listen to
* @param {('statusChange'|'pollQuestionChange'|'missionActionStatusChange')} event the name of the event to stop listen to
* @param {Function} callback the callback function used to register the listener
*/
off(event, callback) {}
Expand Down
47 changes: 0 additions & 47 deletions src/api/user/UserAPISpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ import ExampleUserProvider from '../../../example/exampleUser/ExampleUserProvide
import { createOpenMct, resetApplicationState } from '../../utils/testing.js';
import { MULTIPLE_PROVIDER_ERROR } from './constants.js';

const USERNAME = 'Test User';
const EXAMPLE_ROLE = 'flight';

describe('The User API', () => {
let openmct;

Expand Down Expand Up @@ -65,48 +62,4 @@ describe('The User API', () => {
expect(openmct.user.hasProvider()).toBeTrue();
});
});

describe('provides the ability', () => {
let provider;

beforeEach(() => {
provider = new ExampleUserProvider(openmct);
provider.autoLogin(USERNAME);
});

it('to check if a user (not specific) is logged in', (done) => {
expect(openmct.user.isLoggedIn()).toBeFalse();

openmct.user.on('providerAdded', () => {
expect(openmct.user.isLoggedIn()).toBeTrue();
done();
});

// this will trigger the user indicator plugin,
// which will in turn login the user
openmct.user.setProvider(provider);
});

it('to get the current user', (done) => {
openmct.user.setProvider(provider);
openmct.user
.getCurrentUser()
.then((apiUser) => {
expect(apiUser.name).toEqual(USERNAME);
})
.finally(done);
});

it('to check if a user has a specific role (by id)', (done) => {
openmct.user.setProvider(provider);
let junkIdCheckPromise = openmct.user.hasRole('junk-id').then((hasRole) => {
expect(hasRole).toBeFalse();
});
let realIdCheckPromise = openmct.user.hasRole(EXAMPLE_ROLE).then((hasRole) => {
expect(hasRole).toBeTrue();
});

Promise.all([junkIdCheckPromise, realIdCheckPromise]).finally(done);
});
});
});
9 changes: 2 additions & 7 deletions src/plugins/operatorStatus/operatorStatus/OperatorStatus.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,7 @@
at runtime from the About dialog for additional information.
-->
<template>
<div
:style="position"
class="c-status-poll-panel c-status-poll-panel--operator"
@click.stop="noop"
>
<div :style="position" class="c-status-poll-panel c-status-poll-panel--operator" @click.stop>
<div class="c-status-poll-panel__section c-status-poll-panel__top">
<div class="c-status-poll-panel__title">Status Poll</div>
<div class="c-status-poll-panel__user-role icon-person">{{ role }}</div>
Expand Down Expand Up @@ -191,8 +187,7 @@ export default {
} else {
return status;
}
},
noop() {}
}
}
};
</script>
Loading
Loading