mirror of
https://github.com/finos/SymphonyElectron.git
synced 2025-02-25 18:55:29 -06:00
SDA-1824 (client banner api & cloud config update identifier) (#1345)
* SDA-1824 - client banner api & cloud config updater * SDA-1824 - Expose the new API
This commit is contained in:
parent
f65421dbe1
commit
95b03ff5a1
@ -132,4 +132,149 @@ describe('config', () => {
|
||||
return expect(configInstance.readUserConfig()).rejects.toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('compareCloudConfig', () => {
|
||||
it('should return no updated fields for same objects', () => {
|
||||
const sdaCloudConfig: object = { configVersion: '4.0.0' };
|
||||
const sfeCloudConfig: object = { configVersion: '4.0.0' };
|
||||
const updatedFields = configInstance.compareCloudConfig(
|
||||
sdaCloudConfig,
|
||||
sfeCloudConfig,
|
||||
);
|
||||
|
||||
expect(updatedFields.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should return no updated fields for empty object', () => {
|
||||
const sdaCloudConfig: object = { configVersion: '4.0.0' };
|
||||
const sfeCloudConfig: object = {};
|
||||
const updatedFields = configInstance.compareCloudConfig(
|
||||
sdaCloudConfig,
|
||||
sfeCloudConfig,
|
||||
);
|
||||
|
||||
expect(updatedFields.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should return correct number of updated fields', () => {
|
||||
const sdaCloudConfig: object = {
|
||||
memoryThreshold: false,
|
||||
isCustomTitleBar: true,
|
||||
};
|
||||
const sfeCloudConfig: object = {
|
||||
memoryThreshold: true,
|
||||
isCustomTitleBar: true,
|
||||
};
|
||||
const updatedFields = configInstance.compareCloudConfig(
|
||||
sdaCloudConfig,
|
||||
sfeCloudConfig,
|
||||
);
|
||||
|
||||
expect(updatedFields.length).toBe(1);
|
||||
expect(updatedFields[0]).toBe('memoryThreshold');
|
||||
});
|
||||
|
||||
it('should compare nested object and return correct fields', () => {
|
||||
const sdaCloudConfig: object = {
|
||||
memoryThreshold: false,
|
||||
customFlags: {
|
||||
authNegotiateDelegateWhitelist: '*.symphony.com',
|
||||
authServerWhitelist: '',
|
||||
},
|
||||
};
|
||||
const sfeCloudConfig: object = {
|
||||
memoryThreshold: false,
|
||||
customFlags: {
|
||||
authNegotiateDelegateWhitelist: '*.symphony.com',
|
||||
authServerWhitelist: '',
|
||||
},
|
||||
};
|
||||
const updatedFields = configInstance.compareCloudConfig(
|
||||
sdaCloudConfig,
|
||||
sfeCloudConfig,
|
||||
);
|
||||
|
||||
expect(updatedFields.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should return correct number of updated fields for nested object comparison', () => {
|
||||
const sdaCloudConfig: object = {
|
||||
memoryThreshold: false,
|
||||
customFlags: {
|
||||
authNegotiateDelegateWhitelist: '',
|
||||
authServerWhitelist: '',
|
||||
},
|
||||
};
|
||||
const sfeCloudConfig: object = {
|
||||
memoryThreshold: false,
|
||||
customFlags: {
|
||||
authNegotiateDelegateWhitelist: '*.symphony.com',
|
||||
authServerWhitelist: '',
|
||||
},
|
||||
};
|
||||
const updatedFields = configInstance.compareCloudConfig(
|
||||
sdaCloudConfig,
|
||||
sfeCloudConfig,
|
||||
);
|
||||
|
||||
expect(updatedFields.length).toBe(1);
|
||||
expect(updatedFields[0]).toBe('customFlags');
|
||||
});
|
||||
|
||||
it('should compare array and return correct fields', () => {
|
||||
const sdaCloudConfig: object = {
|
||||
memoryThreshold: false,
|
||||
customFlags: {
|
||||
authNegotiateDelegateWhitelist: '',
|
||||
authServerWhitelist: '',
|
||||
},
|
||||
ctWhitelist: [],
|
||||
podWhitelist: [],
|
||||
};
|
||||
const sfeCloudConfig: object = {
|
||||
memoryThreshold: false,
|
||||
customFlags: {
|
||||
authNegotiateDelegateWhitelist: '',
|
||||
authServerWhitelist: '',
|
||||
},
|
||||
ctWhitelist: [],
|
||||
podWhitelist: [],
|
||||
};
|
||||
const updatedFields = configInstance.compareCloudConfig(
|
||||
sdaCloudConfig,
|
||||
sfeCloudConfig,
|
||||
);
|
||||
|
||||
expect(updatedFields.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should return correct number of updated fields for array comparison', () => {
|
||||
const sdaCloudConfig: object = {
|
||||
memoryThreshold: false,
|
||||
customFlags: {
|
||||
authNegotiateDelegateWhitelist: '',
|
||||
authServerWhitelist: '',
|
||||
},
|
||||
ctWhitelist: ['amulli'],
|
||||
podWhitelist: [],
|
||||
};
|
||||
const sfeCloudConfig: object = {
|
||||
memoryThreshold: false,
|
||||
customFlags: {
|
||||
authNegotiateDelegateWhitelist: '',
|
||||
authServerWhitelist: '',
|
||||
},
|
||||
ctWhitelist: [],
|
||||
podWhitelist: ['butti'],
|
||||
};
|
||||
const updatedFields = configInstance.compareCloudConfig(
|
||||
sdaCloudConfig,
|
||||
sfeCloudConfig,
|
||||
);
|
||||
|
||||
expect(updatedFields.length).toBe(2);
|
||||
expect(updatedFields[0]).toBe('ctWhitelist');
|
||||
expect(updatedFields[1]).toBe('podWhitelist');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -6,7 +6,7 @@ import * as util from 'util';
|
||||
import { buildNumber } from '../../package.json';
|
||||
import { isDevEnv, isElectronQA, isLinux, isMac } from '../common/env';
|
||||
import { logger } from '../common/logger';
|
||||
import { filterOutSelectedValues, pick } from '../common/utils';
|
||||
import { arrayEquals, filterOutSelectedValues, pick } from '../common/utils';
|
||||
|
||||
const writeFile = util.promisify(fs.writeFile);
|
||||
|
||||
@ -15,6 +15,17 @@ export enum CloudConfigDataTypes {
|
||||
ENABLED = 'ENABLED',
|
||||
DISABLED = 'DISABLED',
|
||||
}
|
||||
|
||||
export const ConfigFieldsToRestart = new Set([
|
||||
'permissions',
|
||||
'disableThrottling',
|
||||
'isCustomTitleBar',
|
||||
'ctWhitelist',
|
||||
'podWhitelist',
|
||||
'autoLaunchPath',
|
||||
'customFlags',
|
||||
]);
|
||||
|
||||
export interface IConfig {
|
||||
url: string;
|
||||
minimizeOnClose: CloudConfigDataTypes;
|
||||
@ -66,11 +77,13 @@ export interface IPodLevelEntitlements {
|
||||
disableThrottling: CloudConfigDataTypes;
|
||||
launchOnStartup: CloudConfigDataTypes;
|
||||
memoryThreshold: string;
|
||||
ctWhitelist: string;
|
||||
podWhitelist: string;
|
||||
authNegotiateDelegateWhitelist: string;
|
||||
ctWhitelist: string[];
|
||||
podWhitelist: string[];
|
||||
whitelistUrl: string;
|
||||
authServerWhitelist: string;
|
||||
customFlags: {
|
||||
authNegotiateDelegateWhitelist: string;
|
||||
authServerWhitelist: string;
|
||||
};
|
||||
autoLaunchPath: string;
|
||||
userDataPath: string;
|
||||
}
|
||||
@ -425,6 +438,82 @@ class Config {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the SFE cloud config & SDA Cloud config and returns the unmatched key property
|
||||
* @param sdaCloudConfig Partial<ICloudConfig>
|
||||
* @param sfeCloudConfig Partial<ICloudConfig>
|
||||
*/
|
||||
public compareCloudConfig(
|
||||
sdaCloudConfig: IConfig,
|
||||
sfeCloudConfig: IConfig,
|
||||
): string[] {
|
||||
const updatedField: string[] = [];
|
||||
if (sdaCloudConfig && sfeCloudConfig) {
|
||||
for (const sdaKey in sdaCloudConfig) {
|
||||
if (sdaCloudConfig.hasOwnProperty(sdaKey)) {
|
||||
for (const sfeKey in sfeCloudConfig) {
|
||||
if (sdaKey !== sfeKey) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
Array.isArray(sdaCloudConfig[sdaKey]) &&
|
||||
Array.isArray(sfeCloudConfig[sdaKey])
|
||||
) {
|
||||
if (
|
||||
!arrayEquals(sdaCloudConfig[sdaKey], sfeCloudConfig[sfeKey])
|
||||
) {
|
||||
updatedField.push(sdaKey);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof sdaCloudConfig[sdaKey] === 'object' &&
|
||||
typeof sfeCloudConfig[sfeKey] === 'object'
|
||||
) {
|
||||
for (const sdaObjectKey in sdaCloudConfig[sdaKey]) {
|
||||
if (sdaCloudConfig[sdaKey].hasOwnProperty(sdaObjectKey)) {
|
||||
for (const sfeObjectKey in sfeCloudConfig[sfeKey]) {
|
||||
if (
|
||||
sdaObjectKey === sfeObjectKey &&
|
||||
sdaCloudConfig[sdaKey][sdaObjectKey] !==
|
||||
sfeCloudConfig[sfeKey][sfeObjectKey]
|
||||
) {
|
||||
updatedField.push(sdaKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sdaKey === sfeKey) {
|
||||
if (sdaCloudConfig[sdaKey] !== sfeCloudConfig[sfeKey]) {
|
||||
updatedField.push(sdaKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.info(`config-handler: cloud config updated fields`, [
|
||||
...new Set(updatedField),
|
||||
]);
|
||||
return [...new Set(updatedField)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the different cloud config into config
|
||||
* @param cloudConfig
|
||||
*/
|
||||
public getMergedConfig(cloudConfig: ICloudConfig): object {
|
||||
return {
|
||||
...cloudConfig.acpFeatureLevelEntitlements,
|
||||
...cloudConfig.podLevelEntitlements,
|
||||
...cloudConfig.pmpEntitlements,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* filters out the cloud config
|
||||
*/
|
||||
|
@ -255,39 +255,7 @@ ipcMain.on(
|
||||
analytics.registerPreloadWindow(event.sender);
|
||||
break;
|
||||
case apiCmds.setCloudConfig:
|
||||
const {
|
||||
podLevelEntitlements,
|
||||
acpFeatureLevelEntitlements,
|
||||
pmpEntitlements,
|
||||
...rest
|
||||
} = arg.cloudConfig as ICloudConfig;
|
||||
if (
|
||||
podLevelEntitlements &&
|
||||
podLevelEntitlements.autoLaunchPath &&
|
||||
podLevelEntitlements.autoLaunchPath.match(/\\\\/g)
|
||||
) {
|
||||
podLevelEntitlements.autoLaunchPath = podLevelEntitlements.autoLaunchPath.replace(
|
||||
/\\+/g,
|
||||
'\\',
|
||||
);
|
||||
}
|
||||
if (
|
||||
podLevelEntitlements &&
|
||||
podLevelEntitlements.userDataPath &&
|
||||
podLevelEntitlements.userDataPath.match(/\\\\/g)
|
||||
) {
|
||||
podLevelEntitlements.userDataPath = podLevelEntitlements.userDataPath.replace(
|
||||
/\\+/g,
|
||||
'\\',
|
||||
);
|
||||
}
|
||||
logger.info('main-api-handler: ignored other values from SFE', rest);
|
||||
await config.updateCloudConfig({
|
||||
podLevelEntitlements,
|
||||
acpFeatureLevelEntitlements,
|
||||
pmpEntitlements,
|
||||
});
|
||||
await updateFeaturesForCloudConfig();
|
||||
await updateFeaturesForCloudConfig(arg.cloudConfig as ICloudConfig);
|
||||
if (windowHandler.appMenu) {
|
||||
windowHandler.appMenu.buildMenu();
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ import { autoLaunchInstance } from './auto-launch-controller';
|
||||
import {
|
||||
CloudConfigDataTypes,
|
||||
config,
|
||||
ConfigFieldsToRestart,
|
||||
ICloudConfig,
|
||||
IConfig,
|
||||
ICustomRectangle,
|
||||
} from './config-handler';
|
||||
@ -948,7 +950,64 @@ export const getWindowByName = (
|
||||
});
|
||||
};
|
||||
|
||||
export const updateFeaturesForCloudConfig = async (): Promise<void> => {
|
||||
export const updateFeaturesForCloudConfig = async (
|
||||
cloudConfig: ICloudConfig,
|
||||
): Promise<void> => {
|
||||
const {
|
||||
podLevelEntitlements,
|
||||
acpFeatureLevelEntitlements,
|
||||
pmpEntitlements,
|
||||
...rest
|
||||
} = cloudConfig as ICloudConfig;
|
||||
if (
|
||||
podLevelEntitlements &&
|
||||
podLevelEntitlements.autoLaunchPath &&
|
||||
podLevelEntitlements.autoLaunchPath.match(/\\\\/g)
|
||||
) {
|
||||
podLevelEntitlements.autoLaunchPath = podLevelEntitlements.autoLaunchPath.replace(
|
||||
/\\+/g,
|
||||
'\\',
|
||||
);
|
||||
}
|
||||
if (
|
||||
podLevelEntitlements &&
|
||||
podLevelEntitlements.userDataPath &&
|
||||
podLevelEntitlements.userDataPath.match(/\\\\/g)
|
||||
) {
|
||||
podLevelEntitlements.userDataPath = podLevelEntitlements.userDataPath.replace(
|
||||
/\\+/g,
|
||||
'\\',
|
||||
);
|
||||
}
|
||||
|
||||
logger.info(
|
||||
'window-utils: filtered SDA cloudConfig',
|
||||
config.getMergedConfig(config.cloudConfig as ICloudConfig) as IConfig,
|
||||
);
|
||||
logger.info(
|
||||
'window-utils: filtered SFE cloud config',
|
||||
config.getMergedConfig({
|
||||
podLevelEntitlements,
|
||||
acpFeatureLevelEntitlements,
|
||||
pmpEntitlements,
|
||||
}) as IConfig,
|
||||
);
|
||||
const updatedCloudConfigFields = config.compareCloudConfig(
|
||||
config.getMergedConfig(config.cloudConfig as ICloudConfig) as IConfig,
|
||||
config.getMergedConfig({
|
||||
podLevelEntitlements,
|
||||
acpFeatureLevelEntitlements,
|
||||
pmpEntitlements,
|
||||
}) as IConfig,
|
||||
);
|
||||
|
||||
logger.info('window-utils: ignored other values from SFE', rest);
|
||||
await config.updateCloudConfig({
|
||||
podLevelEntitlements,
|
||||
acpFeatureLevelEntitlements,
|
||||
pmpEntitlements,
|
||||
});
|
||||
|
||||
const {
|
||||
alwaysOnTop: isAlwaysOnTop,
|
||||
launchOnStartup,
|
||||
@ -985,6 +1044,28 @@ export const updateFeaturesForCloudConfig = async (): Promise<void> => {
|
||||
mainWebContents.send('initialize-memory-refresh');
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`window-utils: Updated cloud config fields`,
|
||||
updatedCloudConfigFields,
|
||||
);
|
||||
if (updatedCloudConfigFields && updatedCloudConfigFields.length) {
|
||||
if (mainWebContents && !mainWebContents.isDestroyed()) {
|
||||
const shouldRestart = updatedCloudConfigFields.some((field) =>
|
||||
ConfigFieldsToRestart.has(field),
|
||||
);
|
||||
logger.info(
|
||||
`window-utils: should restart for updated cloud config field?`,
|
||||
shouldRestart,
|
||||
);
|
||||
if (shouldRestart) {
|
||||
mainWebContents.send('display-client-banner', {
|
||||
reason: 'cloudConfig',
|
||||
action: 'restart',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -263,3 +263,5 @@ export type NotificationActionCallback = (
|
||||
event: NotificationActions,
|
||||
data: INotificationData,
|
||||
) => void;
|
||||
|
||||
export type ConfigUpdateType = 'restart' | 'reload';
|
||||
|
@ -261,3 +261,17 @@ export const formatString = (str: string, data?: object): string => {
|
||||
export const calculatePercentage = (value: number, percentage: number) => {
|
||||
return value * percentage * 0.01;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compares two arrays and returns true if they are equal
|
||||
* @param a string[]
|
||||
* @param b string[]
|
||||
*/
|
||||
export const arrayEquals = (a: string[], b: string[]) => {
|
||||
return (
|
||||
Array.isArray(a) &&
|
||||
Array.isArray(b) &&
|
||||
a.length === b.length &&
|
||||
a.every((val, index) => val === b[index])
|
||||
);
|
||||
};
|
||||
|
@ -91,6 +91,7 @@ if (ssfWindow.ssf) {
|
||||
getNativeWindowHandle: ssfWindow.ssf.getNativeWindowHandle,
|
||||
getCitrixMediaRedirectionStatus:
|
||||
ssfWindow.ssf.getCitrixMediaRedirectionStatus,
|
||||
registerClientBanner: ssfWindow.ssf.registerClientBanner,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import { IDownloadItem } from '../app/download-handler';
|
||||
import {
|
||||
apiCmds,
|
||||
apiName,
|
||||
ConfigUpdateType,
|
||||
IBadgeCount,
|
||||
IBoundsChange,
|
||||
ICPUUsage,
|
||||
@ -65,6 +66,9 @@ export interface ILocalObject {
|
||||
collectLogsCallback?: Array<() => void>;
|
||||
analyticsEventHandler?: (arg: any) => void;
|
||||
restartFloater?: (arg: IRestartFloaterData) => void;
|
||||
showClientBannerCallback?: Array<
|
||||
(reason: string, action: ConfigUpdateType) => void
|
||||
>;
|
||||
}
|
||||
|
||||
const local: ILocalObject = {
|
||||
@ -751,6 +755,21 @@ export class SSFApi {
|
||||
cmd: apiCmds.getCitrixMediaRedirectionStatus,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows JS to register a function to display a client banner
|
||||
* @param callback
|
||||
*/
|
||||
public registerClientBanner(
|
||||
callback: (reason: string, action: ConfigUpdateType) => void,
|
||||
): void {
|
||||
if (!local.showClientBannerCallback) {
|
||||
local.showClientBannerCallback = new Array<() => void>();
|
||||
}
|
||||
if (typeof callback === 'function') {
|
||||
local.showClientBannerCallback.push(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -981,6 +1000,18 @@ local.ipcRenderer.on('notification-actions', (_event, args) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* An event triggered by the main process on updating the cloud config
|
||||
* @param {string[]}
|
||||
*/
|
||||
local.ipcRenderer.on('display-client-banner', (_event, args) => {
|
||||
if (local.showClientBannerCallback) {
|
||||
for (const callback of local.showClientBannerCallback) {
|
||||
callback(args.reason, args.action);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Invoked whenever the app is reloaded/navigated
|
||||
const sanitize = (): void => {
|
||||
if (window.name === apiName.mainWindowName) {
|
||||
|
Loading…
Reference in New Issue
Block a user