Skip to content

Commit 1dcdd73

Browse files
authored
Merge pull request #82 from exceptionless/feature/GDPR
Feature/gdpr
2 parents b110dc9 + d48098e commit 1dcdd73

19 files changed

+448
-208
lines changed

src/ExceptionlessClient.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ export class ExceptionlessClient {
2828
constructor(settingsOrApiKey?: IConfigurationSettings | string, serverUrl?: string) {
2929
this.config = typeof settingsOrApiKey === 'object'
3030
? new Configuration(settingsOrApiKey)
31-
: new Configuration({ apiKey: settingsOrApiKey as string, serverUrl });
31+
: new Configuration({ apiKey: settingsOrApiKey as string, serverUrl });
3232

3333
this.updateSettingsTimer(5000);
3434
this.config.onChanged((config) => this.updateSettingsTimer(this._timeoutId > 0 ? 5000 : 0));
35-
this.config.queue.onEventsPosted((events, response) => this.updateSettingsTimer());
35+
this.config.queue.onEventsPosted((events, response) => this.updateSettingsTimer());
3636
}
3737

3838
public createException(exception: Error): EventBuilder {

src/Utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,13 @@ export class Utils {
5959
const result: object = {};
6060

6161
for (const key in defaultValues || {}) {
62-
if (!!defaultValues[key]) {
62+
if (defaultValues[key] !== undefined && defaultValues[key] !== null) {
6363
result[key] = defaultValues[key];
6464
}
6565
}
6666

6767
for (const key in values || {}) {
68-
if (!!values[key]) {
68+
if (values[key] !== undefined && values[key] !== null) {
6969
result[key] = values[key];
7070
}
7171
}

src/configuration/Configuration-spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,31 @@ describe('Configuration', () => {
1919

2020
config = new Configuration({ apiKey: null });
2121
expect(config.apiKey).to.equal('test');
22+
expect(config.includePrivateInformation).to.true;
23+
expect(config.includeUserName).to.true;
24+
expect(config.includeMachineName).to.true;
25+
expect(config.includeIpAddress).to.true;
26+
expect(config.includeCookies).to.true;
27+
expect(config.includePostData).to.true;
28+
expect(config.includeQueryString).to.true;
29+
30+
config = new Configuration({ includePrivateInformation: false });
31+
expect(config.includePrivateInformation).to.false;
32+
expect(config.includeUserName).to.false;
33+
expect(config.includeMachineName).to.false;
34+
expect(config.includeIpAddress).to.false;
35+
expect(config.includeCookies).to.false;
36+
expect(config.includePostData).to.false;
37+
expect(config.includeQueryString).to.false;
38+
39+
config.includeMachineName = true;
40+
expect(config.includePrivateInformation).to.false;
41+
expect(config.includeUserName).to.false;
42+
expect(config.includeMachineName).to.true;
43+
expect(config.includeIpAddress).to.false;
44+
expect(config.includeCookies).to.false;
45+
expect(config.includePostData).to.false;
46+
expect(config.includeQueryString).to.false;
2247
});
2348

2449
it('should not add duplicate plugin', () => {

src/configuration/Configuration.ts

Lines changed: 172 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ export class Configuration implements IConfigurationSettings {
9494
*/
9595
private _serverUrl: string = 'https://collector.exceptionless.io';
9696

97+
/**
98+
* The config server url that all configuration will be retrieved from.
99+
* @type {string}
100+
* @private
101+
*/
102+
private _configServerUrl: string = 'https://config.exceptionless.io';
103+
97104
/**
98105
* The heartbeat server url that all heartbeats will be sent to.
99106
* @type {string}
@@ -115,6 +122,14 @@ export class Configuration implements IConfigurationSettings {
115122
*/
116123
private _dataExclusions: string[] = [];
117124

125+
private _includePrivateInformation: boolean;
126+
private _includeUserName: boolean;
127+
private _includeMachineName: boolean;
128+
private _includeIpAddress: boolean;
129+
private _includeCookies: boolean;
130+
private _includePostData: boolean;
131+
private _includeQueryString: boolean;
132+
118133
/**
119134
* A list of user agent patterns.
120135
* @type {Array}
@@ -146,8 +161,10 @@ export class Configuration implements IConfigurationSettings {
146161
this.log = inject(configSettings.log) || new NullLog();
147162
this.apiKey = configSettings.apiKey;
148163
this.serverUrl = configSettings.serverUrl;
164+
this.configServerUrl = configSettings.configServerUrl;
149165
this.heartbeatServerUrl = configSettings.heartbeatServerUrl;
150166
this.updateSettingsWhenIdleInterval = configSettings.updateSettingsWhenIdleInterval;
167+
this.includePrivateInformation = configSettings.includePrivateInformation;
151168

152169
this.environmentInfoCollector = inject(configSettings.environmentInfoCollector);
153170
this.errorParser = inject(configSettings.errorParser);
@@ -205,12 +222,33 @@ export class Configuration implements IConfigurationSettings {
205222
public set serverUrl(value: string) {
206223
if (!!value) {
207224
this._serverUrl = value;
225+
this._configServerUrl = value;
208226
this._heartbeatServerUrl = value;
209227
this.log.info(`serverUrl: ${value}`);
210228
this.changed();
211229
}
212230
}
213231

232+
/**
233+
* The config server url that all configuration will be retrieved from.
234+
* @returns {string}
235+
*/
236+
public get configServerUrl(): string {
237+
return this._configServerUrl;
238+
}
239+
240+
/**
241+
* The config server url that all configuration will be retrieved from.
242+
* @param value
243+
*/
244+
public set configServerUrl(value: string) {
245+
if (!!value) {
246+
this._configServerUrl = value;
247+
this.log.info(`configServerUrl: ${value}`);
248+
this.changed();
249+
}
250+
}
251+
214252
/**
215253
* The heartbeat server url that all heartbeats will be sent to.
216254
* @returns {string}
@@ -286,6 +324,138 @@ export class Configuration implements IConfigurationSettings {
286324
this._dataExclusions = Utils.addRange<string>(this._dataExclusions, ...exclusions);
287325
}
288326

327+
/**
328+
* Gets a value indicating whether to include private information about the local machine.
329+
* @returns {boolean}
330+
*/
331+
public get includePrivateInformation(): boolean {
332+
return this._includePrivateInformation;
333+
}
334+
335+
/**
336+
* Sets a value indicating whether to include private information about the local machine
337+
* @param value
338+
*/
339+
public set includePrivateInformation(value: boolean) {
340+
const val = value || false;
341+
this._includePrivateInformation = val;
342+
this.includeUserName = val;
343+
this._includeMachineName = val;
344+
this.includeIpAddress = val;
345+
this.includeCookies = val;
346+
this.includePostData = val;
347+
this.includeQueryString = val;
348+
this.changed();
349+
}
350+
351+
/**
352+
* Gets a value indicating whether to include User Name.
353+
* @returns {boolean}
354+
*/
355+
public get includeUserName(): boolean {
356+
return this._includeUserName;
357+
}
358+
359+
/**
360+
* Sets a value indicating whether to include User Name.
361+
* @param value
362+
*/
363+
public set includeUserName(value: boolean) {
364+
this._includeUserName = value || false;
365+
this.changed();
366+
}
367+
368+
/**
369+
* Gets a value indicating whether to include MachineName in MachineInfo.
370+
* @returns {boolean}
371+
*/
372+
public get includeMachineName(): boolean {
373+
return this._includeMachineName;
374+
}
375+
376+
/**
377+
* Sets a value indicating whether to include MachineName in MachineInfo.
378+
* @param value
379+
*/
380+
public set includeMachineName(value: boolean) {
381+
this._includeMachineName = value || false;
382+
this.changed();
383+
}
384+
385+
/**
386+
* Gets a value indicating whether to include Ip Addresses in MachineInfo and RequestInfo.
387+
* @returns {boolean}
388+
*/
389+
public get includeIpAddress(): boolean {
390+
return this._includeIpAddress;
391+
}
392+
393+
/**
394+
* Sets a value indicating whether to include Ip Addresses in MachineInfo and RequestInfo.
395+
* @param value
396+
*/
397+
public set includeIpAddress(value: boolean) {
398+
this._includeIpAddress = value || false;
399+
this.changed();
400+
}
401+
402+
/**
403+
* Gets a value indicating whether to include Cookies.
404+
* NOTE: DataExclusions are applied to all Cookie keys when enabled.
405+
* @returns {boolean}
406+
*/
407+
public get includeCookies(): boolean {
408+
return this._includeCookies;
409+
}
410+
411+
/**
412+
* Sets a value indicating whether to include Cookies.
413+
* NOTE: DataExclusions are applied to all Cookie keys when enabled.
414+
* @param value
415+
*/
416+
public set includeCookies(value: boolean) {
417+
this._includeCookies = value || false;
418+
this.changed();
419+
}
420+
421+
/**
422+
* Gets a value indicating whether to include Form/POST Data.
423+
* NOTE: DataExclusions are only applied to Form data keys when enabled.
424+
* @returns {boolean}
425+
*/
426+
public get includePostData(): boolean {
427+
return this._includePostData;
428+
}
429+
430+
/**
431+
* Sets a value indicating whether to include Form/POST Data.
432+
* NOTE: DataExclusions are only applied to Form data keys when enabled.
433+
* @param value
434+
*/
435+
public set includePostData(value: boolean) {
436+
this._includePostData = value || false;
437+
this.changed();
438+
}
439+
440+
/**
441+
* Gets a value indicating whether to include query string information.
442+
* NOTE: DataExclusions are applied to all Query String keys when enabled.
443+
* @returns {boolean}
444+
*/
445+
public get includeQueryString(): boolean {
446+
return this._includeQueryString;
447+
}
448+
449+
/**
450+
* Sets a value indicating whether to include query string information.
451+
* NOTE: DataExclusions are applied to all Query String keys when enabled.
452+
* @param value
453+
*/
454+
public set includeQueryString(value: boolean) {
455+
this._includeQueryString = value || false;
456+
this.changed();
457+
}
458+
289459
/**
290460
* A list of user agent patterns that will cause any event with a matching user agent to not be submitted.
291461
*
@@ -333,7 +503,7 @@ export class Configuration implements IConfigurationSettings {
333503
*/
334504
public addPlugin(name: string, priority: number, pluginAction: (context: EventPluginContext, next?: () => void) => void): void;
335505
public addPlugin(pluginOrName: IEventPlugin | string, priority?: number, pluginAction?: (context: EventPluginContext, next?: () => void) => void): void {
336-
const plugin: IEventPlugin = !!pluginAction ? { name: pluginOrName as string, priority, run: pluginAction } : pluginOrName as IEventPlugin;
506+
const plugin: IEventPlugin = !!pluginAction ? { name: pluginOrName as string, priority, run: pluginAction } : pluginOrName as IEventPlugin;
337507
if (!plugin || !plugin.run) {
338508
this.log.error('Add plugin failed: Run method not defined');
339509
return;
@@ -468,7 +638,7 @@ export class Configuration implements IConfigurationSettings {
468638
*/
469639
public static get defaults() {
470640
if (Configuration._defaultSettings === null) {
471-
Configuration._defaultSettings = {};
641+
Configuration._defaultSettings = { includePrivateInformation: true };
472642
}
473643

474644
return Configuration._defaultSettings;

src/configuration/IConfigurationSettings.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ import { ISubmissionClient } from '../submission/ISubmissionClient';
1212
export interface IConfigurationSettings {
1313
apiKey?: string;
1414
serverUrl?: string;
15+
configServerUrl?: string;
1516
heartbeatServerUrl?: string;
1617
updateSettingsWhenIdleInterval?: number;
18+
includePrivateInformation?: boolean;
1719
environmentInfoCollector?: IEnvironmentInfoCollector;
1820
errorParser?: IErrorParser;
1921
lastReferenceIdManager?: ILastReferenceIdManager;

src/configuration/SettingsManager.ts

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ interface ISettingsWithVersion {
88
}
99

1010
export class SettingsManager {
11+
private static _isUpdatingSettings: boolean = false;
12+
1113
/**
1214
* A list of handlers that will be fired when the settings change.
1315
* @type {Array}
@@ -50,7 +52,7 @@ export class SettingsManager {
5052
}
5153

5254
public static updateSettings(config: Configuration, version?: number): void {
53-
if (!config || !config.enabled) {
55+
if (!config || !config.enabled || this._isUpdatingSettings) {
5456
return;
5557
}
5658

@@ -65,34 +67,39 @@ export class SettingsManager {
6567
}
6668

6769
config.log.info(`Checking for updated settings from: v${version}.`);
70+
this._isUpdatingSettings = true;
6871
config.submissionClient.getSettings(config, version, (response: SettingsResponse) => {
69-
if (!config || !response || !response.success || !response.settings) {
70-
config.log.warn(`${unableToUpdateMessage}: ${response.message}`);
71-
return;
72-
}
72+
try {
73+
if (!config || !response || !response.success || !response.settings) {
74+
config.log.warn(`${unableToUpdateMessage}: ${response.message}`);
75+
return;
76+
}
7377

74-
config.settings = Utils.merge(config.settings, response.settings);
78+
config.settings = Utils.merge(config.settings, response.settings);
7579

76-
// TODO: Store snapshot of settings after reading from config and attributes and use that to revert to defaults.
77-
// Remove any existing server settings that are not in the new server settings.
78-
const savedServerSettings = SettingsManager.getSavedServerSettings(config);
79-
for (const key in savedServerSettings) {
80-
if (response.settings[key]) {
81-
continue;
82-
}
80+
// TODO: Store snapshot of settings after reading from config and attributes and use that to revert to defaults.
81+
// Remove any existing server settings that are not in the new server settings.
82+
const savedServerSettings = SettingsManager.getSavedServerSettings(config);
83+
for (const key in savedServerSettings) {
84+
if (response.settings[key]) {
85+
continue;
86+
}
8387

84-
delete config.settings[key];
85-
}
88+
delete config.settings[key];
89+
}
8690

87-
const newSettings: ISettingsWithVersion = {
88-
version: response.settingsVersion,
89-
settings: response.settings
90-
};
91+
const newSettings: ISettingsWithVersion = {
92+
version: response.settingsVersion,
93+
settings: response.settings
94+
};
9195

92-
config.storage.settings.save(newSettings);
96+
config.storage.settings.save(newSettings);
9397

94-
config.log.info(`Updated settings: v${newSettings.version}`);
95-
this.changed(config);
98+
config.log.info(`Updated settings: v${newSettings.version}`);
99+
this.changed(config);
100+
} finally {
101+
this._isUpdatingSettings = false;
102+
}
96103
});
97104
}
98105

0 commit comments

Comments
 (0)