Skip to content

Feature/gdpr #82

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

Merged
merged 5 commits into from
May 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions src/ExceptionlessClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ export class ExceptionlessClient {
constructor(settingsOrApiKey?: IConfigurationSettings | string, serverUrl?: string) {
this.config = typeof settingsOrApiKey === 'object'
? new Configuration(settingsOrApiKey)
: new Configuration({ apiKey: settingsOrApiKey as string, serverUrl });
: new Configuration({ apiKey: settingsOrApiKey as string, serverUrl });

this.updateSettingsTimer(5000);
this.config.onChanged((config) => this.updateSettingsTimer(this._timeoutId > 0 ? 5000 : 0));
this.config.queue.onEventsPosted((events, response) => this.updateSettingsTimer());
this.config.queue.onEventsPosted((events, response) => this.updateSettingsTimer());
}

public createException(exception: Error): EventBuilder {
Expand Down
4 changes: 2 additions & 2 deletions src/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@ export class Utils {
const result: object = {};

for (const key in defaultValues || {}) {
if (!!defaultValues[key]) {
if (defaultValues[key] !== undefined && defaultValues[key] !== null) {
result[key] = defaultValues[key];
}
}

for (const key in values || {}) {
if (!!values[key]) {
if (values[key] !== undefined && values[key] !== null) {
result[key] = values[key];
}
}
Expand Down
25 changes: 25 additions & 0 deletions src/configuration/Configuration-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,31 @@ describe('Configuration', () => {

config = new Configuration({ apiKey: null });
expect(config.apiKey).to.equal('test');
expect(config.includePrivateInformation).to.true;
expect(config.includeUserName).to.true;
expect(config.includeMachineName).to.true;
expect(config.includeIpAddress).to.true;
expect(config.includeCookies).to.true;
expect(config.includePostData).to.true;
expect(config.includeQueryString).to.true;

config = new Configuration({ includePrivateInformation: false });
expect(config.includePrivateInformation).to.false;
expect(config.includeUserName).to.false;
expect(config.includeMachineName).to.false;
expect(config.includeIpAddress).to.false;
expect(config.includeCookies).to.false;
expect(config.includePostData).to.false;
expect(config.includeQueryString).to.false;

config.includeMachineName = true;
expect(config.includePrivateInformation).to.false;
expect(config.includeUserName).to.false;
expect(config.includeMachineName).to.true;
expect(config.includeIpAddress).to.false;
expect(config.includeCookies).to.false;
expect(config.includePostData).to.false;
expect(config.includeQueryString).to.false;
});

it('should not add duplicate plugin', () => {
Expand Down
174 changes: 172 additions & 2 deletions src/configuration/Configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ export class Configuration implements IConfigurationSettings {
*/
private _serverUrl: string = 'https://collector.exceptionless.io';

/**
* The config server url that all configuration will be retrieved from.
* @type {string}
* @private
*/
private _configServerUrl: string = 'https://config.exceptionless.io';

/**
* The heartbeat server url that all heartbeats will be sent to.
* @type {string}
Expand All @@ -115,6 +122,14 @@ export class Configuration implements IConfigurationSettings {
*/
private _dataExclusions: string[] = [];

private _includePrivateInformation: boolean;
private _includeUserName: boolean;
private _includeMachineName: boolean;
private _includeIpAddress: boolean;
private _includeCookies: boolean;
private _includePostData: boolean;
private _includeQueryString: boolean;

/**
* A list of user agent patterns.
* @type {Array}
Expand Down Expand Up @@ -146,8 +161,10 @@ export class Configuration implements IConfigurationSettings {
this.log = inject(configSettings.log) || new NullLog();
this.apiKey = configSettings.apiKey;
this.serverUrl = configSettings.serverUrl;
this.configServerUrl = configSettings.configServerUrl;
this.heartbeatServerUrl = configSettings.heartbeatServerUrl;
this.updateSettingsWhenIdleInterval = configSettings.updateSettingsWhenIdleInterval;
this.includePrivateInformation = configSettings.includePrivateInformation;

this.environmentInfoCollector = inject(configSettings.environmentInfoCollector);
this.errorParser = inject(configSettings.errorParser);
Expand Down Expand Up @@ -205,12 +222,33 @@ export class Configuration implements IConfigurationSettings {
public set serverUrl(value: string) {
if (!!value) {
this._serverUrl = value;
this._configServerUrl = value;
this._heartbeatServerUrl = value;
this.log.info(`serverUrl: ${value}`);
this.changed();
}
}

/**
* The config server url that all configuration will be retrieved from.
* @returns {string}
*/
public get configServerUrl(): string {
return this._configServerUrl;
}

/**
* The config server url that all configuration will be retrieved from.
* @param value
*/
public set configServerUrl(value: string) {
if (!!value) {
this._configServerUrl = value;
this.log.info(`configServerUrl: ${value}`);
this.changed();
}
}

/**
* The heartbeat server url that all heartbeats will be sent to.
* @returns {string}
Expand Down Expand Up @@ -286,6 +324,138 @@ export class Configuration implements IConfigurationSettings {
this._dataExclusions = Utils.addRange<string>(this._dataExclusions, ...exclusions);
}

/**
* Gets a value indicating whether to include private information about the local machine.
* @returns {boolean}
*/
public get includePrivateInformation(): boolean {
return this._includePrivateInformation;
}

/**
* Sets a value indicating whether to include private information about the local machine
* @param value
*/
public set includePrivateInformation(value: boolean) {
const val = value || false;
this._includePrivateInformation = val;
this.includeUserName = val;
this._includeMachineName = val;
this.includeIpAddress = val;
this.includeCookies = val;
this.includePostData = val;
this.includeQueryString = val;
this.changed();
}

/**
* Gets a value indicating whether to include User Name.
* @returns {boolean}
*/
public get includeUserName(): boolean {
return this._includeUserName;
}

/**
* Sets a value indicating whether to include User Name.
* @param value
*/
public set includeUserName(value: boolean) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currently nothing using this, but may be useful on node? These were synced from .net client

this._includeUserName = value || false;
this.changed();
}

/**
* Gets a value indicating whether to include MachineName in MachineInfo.
* @returns {boolean}
*/
public get includeMachineName(): boolean {
return this._includeMachineName;
}

/**
* Sets a value indicating whether to include MachineName in MachineInfo.
* @param value
*/
public set includeMachineName(value: boolean) {
this._includeMachineName = value || false;
this.changed();
}

/**
* Gets a value indicating whether to include Ip Addresses in MachineInfo and RequestInfo.
* @returns {boolean}
*/
public get includeIpAddress(): boolean {
return this._includeIpAddress;
}

/**
* Sets a value indicating whether to include Ip Addresses in MachineInfo and RequestInfo.
* @param value
*/
public set includeIpAddress(value: boolean) {
this._includeIpAddress = value || false;
this.changed();
}

/**
* Gets a value indicating whether to include Cookies.
* NOTE: DataExclusions are applied to all Cookie keys when enabled.
* @returns {boolean}
*/
public get includeCookies(): boolean {
return this._includeCookies;
}

/**
* Sets a value indicating whether to include Cookies.
* NOTE: DataExclusions are applied to all Cookie keys when enabled.
* @param value
*/
public set includeCookies(value: boolean) {
this._includeCookies = value || false;
this.changed();
}

/**
* Gets a value indicating whether to include Form/POST Data.
* NOTE: DataExclusions are only applied to Form data keys when enabled.
* @returns {boolean}
*/
public get includePostData(): boolean {
return this._includePostData;
}

/**
* Sets a value indicating whether to include Form/POST Data.
* NOTE: DataExclusions are only applied to Form data keys when enabled.
* @param value
*/
public set includePostData(value: boolean) {
this._includePostData = value || false;
this.changed();
}

/**
* Gets a value indicating whether to include query string information.
* NOTE: DataExclusions are applied to all Query String keys when enabled.
* @returns {boolean}
*/
public get includeQueryString(): boolean {
return this._includeQueryString;
}

/**
* Sets a value indicating whether to include query string information.
* NOTE: DataExclusions are applied to all Query String keys when enabled.
* @param value
*/
public set includeQueryString(value: boolean) {
this._includeQueryString = value || false;
this.changed();
}

/**
* A list of user agent patterns that will cause any event with a matching user agent to not be submitted.
*
Expand Down Expand Up @@ -333,7 +503,7 @@ export class Configuration implements IConfigurationSettings {
*/
public addPlugin(name: string, priority: number, pluginAction: (context: EventPluginContext, next?: () => void) => void): void;
public addPlugin(pluginOrName: IEventPlugin | string, priority?: number, pluginAction?: (context: EventPluginContext, next?: () => void) => void): void {
const plugin: IEventPlugin = !!pluginAction ? { name: pluginOrName as string, priority, run: pluginAction } : pluginOrName as IEventPlugin;
const plugin: IEventPlugin = !!pluginAction ? { name: pluginOrName as string, priority, run: pluginAction } : pluginOrName as IEventPlugin;
if (!plugin || !plugin.run) {
this.log.error('Add plugin failed: Run method not defined');
return;
Expand Down Expand Up @@ -468,7 +638,7 @@ export class Configuration implements IConfigurationSettings {
*/
public static get defaults() {
if (Configuration._defaultSettings === null) {
Configuration._defaultSettings = {};
Configuration._defaultSettings = { includePrivateInformation: true };
}

return Configuration._defaultSettings;
Expand Down
2 changes: 2 additions & 0 deletions src/configuration/IConfigurationSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import { ISubmissionClient } from '../submission/ISubmissionClient';
export interface IConfigurationSettings {
apiKey?: string;
serverUrl?: string;
configServerUrl?: string;
heartbeatServerUrl?: string;
updateSettingsWhenIdleInterval?: number;
includePrivateInformation?: boolean;
environmentInfoCollector?: IEnvironmentInfoCollector;
errorParser?: IErrorParser;
lastReferenceIdManager?: ILastReferenceIdManager;
Expand Down
51 changes: 29 additions & 22 deletions src/configuration/SettingsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ interface ISettingsWithVersion {
}

export class SettingsManager {
private static _isUpdatingSettings: boolean = false;

/**
* A list of handlers that will be fired when the settings change.
* @type {Array}
Expand Down Expand Up @@ -50,7 +52,7 @@ export class SettingsManager {
}

public static updateSettings(config: Configuration, version?: number): void {
if (!config || !config.enabled) {
if (!config || !config.enabled || this._isUpdatingSettings) {
return;
}

Expand All @@ -65,34 +67,39 @@ export class SettingsManager {
}

config.log.info(`Checking for updated settings from: v${version}.`);
this._isUpdatingSettings = true;
config.submissionClient.getSettings(config, version, (response: SettingsResponse) => {
if (!config || !response || !response.success || !response.settings) {
config.log.warn(`${unableToUpdateMessage}: ${response.message}`);
return;
}
try {
if (!config || !response || !response.success || !response.settings) {
config.log.warn(`${unableToUpdateMessage}: ${response.message}`);
return;
}

config.settings = Utils.merge(config.settings, response.settings);
config.settings = Utils.merge(config.settings, response.settings);

// TODO: Store snapshot of settings after reading from config and attributes and use that to revert to defaults.
// Remove any existing server settings that are not in the new server settings.
const savedServerSettings = SettingsManager.getSavedServerSettings(config);
for (const key in savedServerSettings) {
if (response.settings[key]) {
continue;
}
// TODO: Store snapshot of settings after reading from config and attributes and use that to revert to defaults.
// Remove any existing server settings that are not in the new server settings.
const savedServerSettings = SettingsManager.getSavedServerSettings(config);
for (const key in savedServerSettings) {
if (response.settings[key]) {
continue;
}

delete config.settings[key];
}
delete config.settings[key];
}

const newSettings: ISettingsWithVersion = {
version: response.settingsVersion,
settings: response.settings
};
const newSettings: ISettingsWithVersion = {
version: response.settingsVersion,
settings: response.settings
};

config.storage.settings.save(newSettings);
config.storage.settings.save(newSettings);

config.log.info(`Updated settings: v${newSettings.version}`);
this.changed(config);
config.log.info(`Updated settings: v${newSettings.version}`);
this.changed(config);
} finally {
this._isUpdatingSettings = false;
}
});
}

Expand Down
Loading