@microsoft/windows-admin-center-sdk
Version:
Microsoft - Windows Admin Center Shell
374 lines (372 loc) • 14.1 kB
JavaScript
import { EMPTY, ReplaySubject } from 'rxjs';
import { Globalization } from '../data/globalization';
import { Net } from '../data/net';
import { LogLevel } from '../diagnostics/log-level';
import { Logging } from '../diagnostics/logging';
import { SmeWebTelemetry } from '../diagnostics/sme-web-telemetry';
import { RpcNotificationSubjectServer } from '../rpc/notification/rpc-notification-subject-server';
import { RpcWorkItemFindSubjectServer } from '../rpc/work-item-find/rpc-work-item-find-subject-server';
import { Notification, NotificationChangeEvent } from './notification';
import { NotificationState } from './notification-state';
/**
* Mock the IFrameService class in app folder
*/
export class IFrameService {
getActiveToolIFrameData;
}
/**
* Notification manager class.
*/
export class NotificationManager {
rpc;
collection;
psSessionIdToNotificationIdMap = new Map();
notificationIdToPsSessionIdMap = new Map();
rpcNotifySubscription;
rpcQuerySubscription;
changedEventSubject;
rpcWorkItemFindSubjectServer;
rpcNotificationSubjectServer;
iFrameService;
/**
* Initializes a new instance of the NotificationManager class.
*
* @param rpc the RPC object.
*/
constructor(rpc) {
this.rpc = rpc;
this.changedEventSubject = new ReplaySubject(1);
this.initialize();
}
/**
* register iframe service from shell
* @param iFrameService the iframe service from shell
*/
registerIFrameService(iFrameService) {
this.iFrameService = iFrameService;
}
/**
* Gets the items from current notification collection including dismissed.
*/
get items() {
const items = [];
for (const item in this.collection) {
if (this.collection.hasOwnProperty(item)) {
items.push(this.collection[item]);
}
}
return items;
}
/**
* Gets the subject of notification changed event.
*/
get changed() {
return this.changedEventSubject;
}
/**
* Initializes the rpc notification call.
*/
initialize() {
this.collection = {};
// notification request from rpc...
this.rpcNotificationSubjectServer = new RpcNotificationSubjectServer(this.rpc);
this.rpcNotifySubscription = this.rpcNotificationSubjectServer.subject
.subscribe({
next: item => {
this.notify(item.data).toPromise().then(item.deferred.resolve, item.deferred.reject);
},
error: error => {
const message = MsftSme.getStrings().MsftSmeShell.Core.Error.NotificationRpcInitialization.message;
Logging.log({
source: 'Notification',
level: LogLevel.Error,
message: message.format(Net.getErrorMessage(error))
});
}
});
this.rpcWorkItemFindSubjectServer = new RpcWorkItemFindSubjectServer(this.rpc);
this.rpcQuerySubscription = this.rpcWorkItemFindSubjectServer.subject
.subscribe({
next: item => {
item.deferred.resolve(this.workItemFind(item.data));
},
error: error => {
const message = MsftSme.getStrings().MsftSmeShell.Core.Error.NotificationRpcInitialization.message;
Logging.log({
source: 'Notification',
level: LogLevel.Error,
message: message.format(Net.getErrorMessage(error))
});
}
});
this.addEvent(NotificationChangeEvent.Initialized);
}
/**
* Stop the notification manager.
*/
uninitialize() {
if (this.rpcNotifySubscription) {
this.rpcNotifySubscription.unsubscribe();
this.rpcNotifySubscription = null;
}
if (this.rpcQuerySubscription) {
this.rpcQuerySubscription.unsubscribe();
this.rpcQuerySubscription = null;
}
}
/**
* Find a notification.
*
* @param id the notification id.
*/
find(id) {
const notificationId = this.psSessionIdToNotificationIdMap[id] || id;
return this.collection[notificationId];
}
/**
* Remove a notification.
* There is no dismiss API on the gateway, this just remove from the list.
* Don't remove active notification. Use dismiss api instead, so it doesn't displays to .items property.
*
* @param id the session id (notification id).
* @return boolean true if removed.
*/
remove(id) {
const notification = this.find(id);
if (notification) {
const psSessionId = this.notificationIdToPsSessionIdMap[id];
delete this.psSessionIdToNotificationIdMap[psSessionId];
delete this.notificationIdToPsSessionIdMap[id];
delete this.collection[id];
this.addEvent(NotificationChangeEvent.Remove, notification);
return true;
}
return false;
}
/**
* Dismiss a notification to mark dismiss property.
*
* @param id the session id (notification id).
* @return boolean true if dismissed.
*/
dismiss(id) {
const notification = this.find(id);
if (notification) {
notification.dismissed = true;
this.addEvent(NotificationChangeEvent.Remove, notification);
return true;
}
return false;
}
/**
* Set dismissed to false for notification updated that was previously dismissed
*
* @param id the session id (notification id).
* @return boolean true if undismissed.
*/
undismiss(id) {
const notification = this.find(id);
if (notification) {
notification.dismissed = false;
this.addEvent(NotificationChangeEvent.Change, notification);
return true;
}
return false;
}
/**
* Add notification from WorkItem.
*
* @param psSessionId the psSession ID.
* @param workItem the RPC work item.
* @param state the initial state.
* @param object the object from query result.
* @return notification the notification object.
*/
addFromWorkItem(notificationId, workItem, state) {
const notification = Notification.createFromWorkItem(notificationId, workItem, state, this.iFrameService);
const existingNotification = this.find(notificationId);
this.collection[notificationId] = notification;
if (!notification.isDisabled) {
if (existingNotification) {
this.addEvent(NotificationChangeEvent.Change, notification);
}
else {
this.addEvent(NotificationChangeEvent.Add, notification);
}
}
}
/**
* Update an existing work item with psSession Id information.
*
* @param notificationId the id of the notification.
* @param psSessionId the psSession ID.
* @param workItem the work item.
* @param state the state of the work item.
* @param object the object from query result.
*/
updateWorkItemWithPsSession(notificationId, psSessionId, workItem, state, object) {
const notification = this.find(notificationId);
this.psSessionIdToNotificationIdMap[psSessionId] = notificationId;
this.notificationIdToPsSessionIdMap[notificationId] = psSessionId;
if (notification) {
if (notification.updateFromWorkItem(notificationId, workItem, state, object)) {
this.addEvent(NotificationChangeEvent.Change, notification);
}
return true;
}
return false;
}
/**
* Add notification from Recover.
*
* @param id the notification ID.
* @param workItem the RPC work item.
* @param state the initial state.
* @param object the object from query result.
* @return notification the notification object.
*/
addFromRecover(recover) {
const notification = Notification.createFromRecover(recover, this.iFrameService);
this.collection[recover.id] = notification;
this.psSessionIdToNotificationIdMap[recover.id] = recover.id;
this.notificationIdToPsSessionIdMap[recover.id] = recover.id;
this.addEvent(NotificationChangeEvent.Add, notification);
}
/**
* Add initial notification for Message Notification.
*
* @param id the notification ID.
* @param workItem the RPC work item.
* @param state the initial state.
* @param object the object from query result.
* @return notification the notification object.
*/
addForNotificationMessage(state, message) {
if (this.collection[message.id]) {
return false;
}
const notification = new Notification(message.id);
const now = new Date();
notification.isFromRecover = true;
notification.state = state;
notification.object = {};
notification.nodeName = message.sourceName;
notification.moduleDisplayName = message.sourceName;
notification.startTimestamp = Globalization.timeOnly(now);
notification.changedTimestamp = Globalization.timeOnly(now);
notification.changedTimestampValue = now.getTime();
notification.description = null;
notification.typeId = null;
notification.title = message.title;
notification.message = message.message;
this.collection[message.id] = notification;
this.psSessionIdToNotificationIdMap[message.id] = message.id;
this.notificationIdToPsSessionIdMap[message.id] = message.id;
this.addEvent(NotificationChangeEvent.Add, notification);
return true;
}
/**
* Update notification from socket message.
*
* @param psSessionId the psSession ID.
* @param message the socket message.
*/
updateFromMessage(psSessionId, message) {
const notificationId = this.psSessionIdToNotificationIdMap[psSessionId];
const notification = this.find(notificationId);
if (notification) {
if (notification.updateFromMessage(message)) {
this.addEvent(NotificationChangeEvent.Change, notification);
}
return true;
}
return false;
}
/**
* Update notification from socket message.
*
* @param psSessionId the psSession ID.
* @param message the socket message.
*/
updateFromNotificationMessage(state, item) {
const notification = this.find(item.id);
if (notification) {
notification.updateFromNotificationMessage(state, item);
this.addEvent(NotificationChangeEvent.Change, notification);
return true;
}
return false;
}
/**
* Add or update client notification.
*
* @param clientNotification the client notification object.
* @param Observable the observable of void.
*/
notify(clientNotification) {
// convert alert into notification
if (MsftSme.isNullOrWhiteSpace(clientNotification.title)) {
clientNotification.title = clientNotification.message;
clientNotification.message = null;
}
SmeWebTelemetry.traceClientNotification(clientNotification);
let notification = this.find(clientNotification.id);
if (notification) {
if (notification.updateFromClient(clientNotification)) {
this.addEvent(NotificationChangeEvent.Change, notification);
}
return EMPTY;
}
notification = Notification.createFromClient(clientNotification, this.iFrameService);
this.collection[clientNotification.id] = notification;
this.addEvent(NotificationChangeEvent.Add, notification);
return EMPTY;
}
/**
* Find current work item by the typeId/sourceName/nodeName.
*
* @param workItemFind the query notification object.
* @param RpcWorkItemFindResult the result of query.
*/
workItemFind(workItemFind) {
const keys = Object.keys(this.collection);
const results = keys
.map(key => ({ key: key, notification: this.collection[key] }))
.filter(data => data.notification.moduleName === workItemFind.moduleName
&& data.notification.nodeName === workItemFind.nodeName
&& data.notification.typeId === workItemFind.typeId)
.map(data => ({
id: data.key,
state: data.notification.state,
percent: data.notification.percent,
error: data.notification.error,
object: data.notification.object
}));
const notificationResult = {
results: results,
typeId: workItemFind.typeId,
moduleName: workItemFind.moduleName,
nodeName: workItemFind.nodeName
};
return notificationResult;
}
/**
* Add an event to report the change of notification data or collection.
*
* @param changeEvent the changed event.
* @param notification the notification object. (optional)
*/
addEvent(changeEvent, notification) {
// need to copy otherwise every change to original notification will be shown on UI without event
const notificationCopy = {};
if (notification) {
MsftSme.shallowCopyFromObject(notificationCopy, notification);
}
this.changedEventSubject.next({ notification: notificationCopy, changeEvent: changeEvent });
// no localization.
Logging.logVerbose('Notification', notification ?
'addEvent: {0}/{1}\n{2}\n{3}\n{4}'.format(NotificationChangeEvent[changeEvent], NotificationState[notification.state], notification.title, notification.message, notification.link)
: 'addEvent: {0}'.format(NotificationChangeEvent[changeEvent]));
}
}
//# sourceMappingURL=notification-manager.js.map