From 026fa16f1ca6218c84d83a123e0bd8b140e5c331 Mon Sep 17 00:00:00 2001 From: Nev Wylie <54870357+MSNev@users.noreply.github.com> Date: Tue, 14 Mar 2023 14:29:57 -0700 Subject: [PATCH] [Main] Fix extension / channel not being readonly - Bug fixes for getPlugin() not searching the TeeChannel - Minification improcements --- .../src/Sender.ts | 26 +-- channels/tee-channel-js/src/TeeChannel.ts | 21 +- examples/shared-worker/package.json | 4 +- .../src/JavaScriptSDK/AnalyticsPlugin.ts | 19 +- .../Telemetry/PageVisitTimeManager.ts | 36 +--- .../src/ClickAnalyticsPlugin.ts | 25 +-- .../src/Context/User.ts | 8 +- .../src/PropertiesPlugin.ts | 8 +- .../src/Interfaces/IChannelControlsAI.ts | 7 - .../src/applicationinsights-common.ts | 1 - .../Tests/Unit/src/Dynamic.Tests.ts | 100 +-------- .../Tests/Unit/src/DynamicConfig.Tests.ts | 50 +++++ .../Tests/Unit/src/EventHelper.Tests.ts | 6 +- .../Tests/Unit/src/TestPlugins.ts | 197 +++++++++++++++++ .../Tests/Unit/src/UpdateConfig.Tests.ts | 198 +----------------- .../src/Config/DynamicConfig.ts | 12 +- .../src/Config/DynamicProperty.ts | 6 +- .../IChannelControlsHost.ts | 13 ++ .../IConfiguration.ts | 4 +- .../ITelemetryPlugin.ts | 2 +- .../src/JavaScriptSDK/AppInsightsCore.ts | 66 +++--- .../src/JavaScriptSDK/DataCacheHelper.ts | 30 +-- .../src/JavaScriptSDK/HelperFuncs.ts | 20 +- .../src/JavaScriptSDK/PerfManager.ts | 20 +- .../src/applicationinsights-core-js.ts | 1 + 25 files changed, 412 insertions(+), 468 deletions(-) delete mode 100644 shared/AppInsightsCommon/src/Interfaces/IChannelControlsAI.ts create mode 100644 shared/AppInsightsCore/Tests/Unit/src/TestPlugins.ts create mode 100644 shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IChannelControlsHost.ts diff --git a/channels/applicationinsights-channel-js/src/Sender.ts b/channels/applicationinsights-channel-js/src/Sender.ts index 00e7c9a72..04ee6e251 100644 --- a/channels/applicationinsights-channel-js/src/Sender.ts +++ b/channels/applicationinsights-channel-js/src/Sender.ts @@ -1,18 +1,18 @@ import dynamicProto from "@microsoft/dynamicproto-js"; import { - BreezeChannelIdentifier, DEFAULT_BREEZE_ENDPOINT, DEFAULT_BREEZE_PATH, DisabledPropertyName, Event, Exception, IChannelControlsAI, - IConfig, IEnvelope, ISample, Metric, PageView, PageViewPerformance, ProcessLegacy, RemoteDependencyData, RequestHeaders, SampleRate, - Trace, eRequestHeaders, isInternalApplicationInsightsEndpoint, utlCanUseSessionStorage + BreezeChannelIdentifier, DEFAULT_BREEZE_ENDPOINT, DEFAULT_BREEZE_PATH, DisabledPropertyName, Event, Exception, IConfig, IEnvelope, + ISample, Metric, PageView, PageViewPerformance, ProcessLegacy, RemoteDependencyData, RequestHeaders, SampleRate, Trace, eRequestHeaders, + isInternalApplicationInsightsEndpoint, utlCanUseSessionStorage } from "@microsoft/applicationinsights-common"; import { - BaseTelemetryPlugin, IAppInsightsCore, IConfigDefaults, IConfiguration, IDiagnosticLogger, INotificationManager, IPlugin, - IProcessTelemetryContext, IProcessTelemetryUnloadContext, ITelemetryItem, ITelemetryPluginChain, ITelemetryUnloadState, + BaseTelemetryPlugin, IAppInsightsCore, IChannelControls, IConfigDefaults, IConfiguration, IDiagnosticLogger, INotificationManager, + IPlugin, IProcessTelemetryContext, IProcessTelemetryUnloadContext, ITelemetryItem, ITelemetryPluginChain, ITelemetryUnloadState, SendRequestReason, _eInternalMessageId, _throwInternal, _warnToConsole, arrForEach, cfgDfBoolean, cfgDfValidate, createProcessTelemetryContext, createUniqueNamespace, dateNow, dumpObj, eLoggingSeverity, getExceptionName, getIEVersion, getJSON, getNavigator, getWindow, isArray, isBeaconsSupported, isFetchSupported, isNullOrUndefined, isXhrSupported, mergeEvtNamespace, objExtend, objKeys, onConfigChange, useXDomainRequest } from "@microsoft/applicationinsights-core-js"; -import { ITimerHandler, isTruthy, objDeepFreeze, objDefineProp, scheduleTimeout } from "@nevware21/ts-utils"; +import { ITimerHandler, isTruthy, objDeepFreeze, objDefine, scheduleTimeout } from "@nevware21/ts-utils"; import { DependencyEnvelopeCreator, EventEnvelopeCreator, ExceptionEnvelopeCreator, MetricEnvelopeCreator, PageViewEnvelopeCreator, PageViewPerformanceEnvelopeCreator, TraceEnvelopeCreator @@ -81,7 +81,7 @@ const EnvelopeTypeCreator: { [key:string] : EnvelopeCreator } = { [RemoteDependencyData.dataType]: DependencyEnvelopeCreator }; -export class Sender extends BaseTelemetryPlugin implements IChannelControlsAI { +export class Sender extends BaseTelemetryPlugin implements IChannelControls { public static constructEnvelope(orig: ITelemetryItem, iKey: string, logger: IDiagnosticLogger, convertUndefined?: any): IEnvelope { let envelope: ITelemetryItem; @@ -235,10 +235,8 @@ export class Sender extends BaseTelemetryPlugin implements IChannelControlsAI { let ctx = createProcessTelemetryContext(null, config, core); let senderConfig = ctx.getExtCfg(identifier, defaultAppInsightsChannelConfig); - objDefineProp(_self, "_senderConfig", { - enumerable: true, - configurable: true, - get: function() { + objDefine(_self, "_senderConfig", { + g: function() { return senderConfig; } }); @@ -1152,10 +1150,8 @@ export class Sender extends BaseTelemetryPlugin implements IChannelControlsAI { _sessionStorageUsed = null; _namePrefix = UNDEFINED_VALUE; - objDefineProp(_self, "_senderConfig", { - enumerable: true, - configurable: true, - get: function() { + objDefine(_self, "_senderConfig", { + g: function() { return objExtend({}, defaultAppInsightsChannelConfig); } }); diff --git a/channels/tee-channel-js/src/TeeChannel.ts b/channels/tee-channel-js/src/TeeChannel.ts index ab0d1d4a5..0283aefa3 100644 --- a/channels/tee-channel-js/src/TeeChannel.ts +++ b/channels/tee-channel-js/src/TeeChannel.ts @@ -2,11 +2,12 @@ // Licensed under the MIT License. import dynamicProto from "@microsoft/dynamicproto-js"; -import { IChannelControlsAI, IConfig } from "@microsoft/applicationinsights-common"; +import { IConfig } from "@microsoft/applicationinsights-common"; import { - BaseTelemetryPlugin, IAppInsightsCore, IChannelControls, IConfigDefaults, IConfiguration, IPlugin, IProcessTelemetryContext, - IProcessTelemetryUnloadContext, IProcessTelemetryUpdateContext, ITelemetryItem, ITelemetryPluginChain, ITelemetryUnloadState, - ITelemetryUpdateState, cfgDfBoolean, createProcessTelemetryContext, initializePlugins, onConfigChange, proxyFunctions + BaseTelemetryPlugin, IAppInsightsCore, IChannelControls, IChannelControlsHost, IConfigDefaults, IConfiguration, IPlugin, + IProcessTelemetryContext, IProcessTelemetryUnloadContext, IProcessTelemetryUpdateContext, ITelemetryItem, ITelemetryPluginChain, + ITelemetryUnloadState, ITelemetryUpdateState, cfgDfBoolean, createProcessTelemetryContext, initializePlugins, onConfigChange, + proxyFunctions } from "@microsoft/applicationinsights-core-js"; import { arrForEach, isArray, objDeepFreeze, objFreeze, throwError } from "@nevware21/ts-utils"; import { ChannelControllerPriority, IChannelController, _IInternalChannels, createChannelControllerPlugin } from "./ChannelController"; @@ -64,7 +65,7 @@ function _createChannelQueues(config: ITeeChannelConfig, core: IAppInsightsCore, return channelQueue; } -export class TeeChannel extends BaseTelemetryPlugin implements IChannelControlsAI { +export class TeeChannel extends BaseTelemetryPlugin implements IChannelControlsHost { public readonly identifier: string = "TeeChannelController"; public readonly priority: number = 999; @@ -215,4 +216,14 @@ export class TeeChannel extends BaseTelemetryPlugin implements IChannelControlsA public onunloadFlush() { // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging } + + /** + * Get and return the named channel instance (if present) from the queues + * @param pluginIdentifier - The identifier name of the plugin + */ + public getChannel(pluginIdentifier: string): T { + // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging + return null; + } + } diff --git a/examples/shared-worker/package.json b/examples/shared-worker/package.json index 70e4e8edb..9983e5743 100644 --- a/examples/shared-worker/package.json +++ b/examples/shared-worker/package.json @@ -27,8 +27,8 @@ "mintest": "", "perftest": "", "lint": "tslint -p tsconfig.json", - "ai-min": "grunt example-shared-worker-min", - "ai-restore": "grunt example-shared-worker-restore" + "ai-min": "", + "ai-restore": "" }, "repository": { "type": "git", diff --git a/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/AnalyticsPlugin.ts b/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/AnalyticsPlugin.ts index b7c486180..a0d9e0487 100644 --- a/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/AnalyticsPlugin.ts +++ b/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/AnalyticsPlugin.ts @@ -17,11 +17,10 @@ import { ITelemetryPluginChain, ITelemetryUnloadState, InstrumentEvent, TelemetryInitializerFunction, _eInternalMessageId, arrForEach, cfgDfBoolean, cfgDfSet, cfgDfString, cfgDfValidate, createProcessTelemetryContext, createUniqueNamespace, dumpObj, eLoggingSeverity, eventOff, eventOn, generateW3CId, getDocument, getExceptionName, getHistory, getLocation, getWindow, hasHistory, hasWindow, isFunction, - isNullOrUndefined, isString, isUndefined, mergeEvtNamespace, objDefineAccessors, onConfigChange, safeGetCookieMgr, strUndefined, - throwError + isNullOrUndefined, isString, isUndefined, mergeEvtNamespace, onConfigChange, safeGetCookieMgr, strUndefined, throwError } from "@microsoft/applicationinsights-core-js"; import { PropertiesPlugin } from "@microsoft/applicationinsights-properties-js"; -import { isError, objDeepFreeze, objDefineProp, scheduleTimeout, strIndexOf } from "@nevware21/ts-utils"; +import { isError, objDeepFreeze, objDefine, scheduleTimeout, strIndexOf } from "@nevware21/ts-utils"; import { IAppInsightsInternal, PageViewManager } from "./Telemetry/PageViewManager"; import { PageViewPerformanceManager } from "./Telemetry/PageViewPerformanceManager"; import { PageVisitTimeManager } from "./Telemetry/PageVisitTimeManager"; @@ -890,18 +889,16 @@ export class AnalyticsPlugin extends BaseTelemetryPlugin implements IAppInsights _extConfig = null; // Define _self.config - objDefineProp(_self, "config", { - configurable: true, - enumerable: true, - get: () => _extConfig + objDefine(_self, "config", { + g: () => _extConfig }); } // For backward compatibility - objDefineAccessors(_self, "_pageViewManager", () => _pageViewManager); - objDefineAccessors(_self, "_pageViewPerformanceManager", () => _pageViewPerformanceManager); - objDefineAccessors(_self, "_pageVisitTimeManager", () => _pageVisitTimeManager); - objDefineAccessors(_self, "_evtNamespace", () => "." + _evtNamespace); + objDefine(_self, "_pageViewManager", { g: () => _pageViewManager }); + objDefine(_self, "_pageViewPerformanceManager", { g: () => _pageViewPerformanceManager }); + objDefine(_self, "_pageVisitTimeManager", { g: () => _pageVisitTimeManager }); + objDefine(_self, "_evtNamespace", { g: () => "." + _evtNamespace }); }); } diff --git a/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/Telemetry/PageVisitTimeManager.ts b/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/Telemetry/PageVisitTimeManager.ts index 9fd53c794..a2357ef45 100644 --- a/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/Telemetry/PageVisitTimeManager.ts +++ b/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/Telemetry/PageVisitTimeManager.ts @@ -6,8 +6,9 @@ import { utlCanUseSessionStorage, utlGetSessionStorage, utlRemoveSessionStorage, utlSetSessionStorage } from "@microsoft/applicationinsights-common"; import { - IDiagnosticLogger, _warnToConsole, dateNow, dumpObj, getJSON, hasJSON, objDefineAccessors, throwError + IDiagnosticLogger, _warnToConsole, dateNow, dumpObj, getJSON, hasJSON, throwError } from "@microsoft/applicationinsights-core-js"; +import { objDefine } from "@nevware21/ts-utils"; /** * Used to track page visit durations @@ -47,38 +48,23 @@ export class PageVisitTimeManager { let prevPageVisitData: PageVisitData = null; try { prevPageVisitData = stopPageVisitTimer(); - startPageVisitTimer(pageName, pageUrl); - - } catch (e) { - _warnToConsole(logger, "Call to restart failed: " + dumpObj(e)); - prevPageVisitData = null; - } - - return prevPageVisitData; - } - - /** - * Starts timing visit duration of pageName - * @param pageName - * @returns {} - */ - function startPageVisitTimer(pageName: string, pageUrl: string) { - try { if (utlCanUseSessionStorage()) { if (utlGetSessionStorage(logger, prevPageVisitDataKeyName) != null) { throwError("Cannot call startPageVisit consecutively without first calling stopPageVisit"); } - const currPageVisitData = new PageVisitData(pageName, pageUrl); - const currPageVisitDataStr = getJSON().stringify(currPageVisitData); + const currPageVisitDataStr = getJSON().stringify(new PageVisitData(pageName, pageUrl)); utlSetSessionStorage(logger, prevPageVisitDataKeyName, currPageVisitDataStr); } + } catch (e) { - // TODO: Remove this catch in next phase, since if start is called twice in a row the exception needs to be propagated out - _warnToConsole(logger, "Call to start failed: " + dumpObj(e)); + _warnToConsole(logger, "Call to restart failed: " + dumpObj(e)); + prevPageVisitData = null; } + + return prevPageVisitData; } - + /** * Stops timing of current page, if exists. * @returns {PageVisitData} Page visit data (including duration) of pageName from call to start, if exists. Null if not. @@ -112,8 +98,8 @@ export class PageVisitTimeManager { } // For backward compatibility - objDefineAccessors(_self, "_logger", () => logger); - objDefineAccessors(_self, "pageVisitTimeTrackingHandler", () => pageVisitTimeTrackingHandler); + objDefine(_self, "_logger", { g: () => logger }); + objDefine(_self, "pageVisitTimeTrackingHandler", { g: () => pageVisitTimeTrackingHandler}); }); } diff --git a/extensions/applicationinsights-clickanalytics-js/src/ClickAnalyticsPlugin.ts b/extensions/applicationinsights-clickanalytics-js/src/ClickAnalyticsPlugin.ts index fdf5dc89e..19ad18ac6 100644 --- a/extensions/applicationinsights-clickanalytics-js/src/ClickAnalyticsPlugin.ts +++ b/extensions/applicationinsights-clickanalytics-js/src/ClickAnalyticsPlugin.ts @@ -11,7 +11,7 @@ import { getExceptionName, isNullOrUndefined, onConfigChange, throwError, unloadComponents } from "@microsoft/applicationinsights-core-js"; import { PropertiesPlugin } from "@microsoft/applicationinsights-properties-js"; -import { hasDocument, objDeepFreeze, objDefineProp } from "@nevware21/ts-utils"; +import { hasDocument, objDeepFreeze } from "@nevware21/ts-utils"; import { IAutoCaptureHandler, IClickAnalyticsConfiguration, IContentHandler, ICoreData, ICustomDataTags, IPageActionTelemetry, IValueCallback } from "./Interfaces/Datamodel"; @@ -153,22 +153,15 @@ export class ClickAnalyticsPlugin extends BaseTelemetryPlugin { _autoCapture = autoCapture; })); } - }); - function _initDefaults() { - _config = null; - _pageAction = null; - _autoCaptureHandler = null; - _contentHandler = null; - _autoCapture = false; - - // Define _self.config - objDefineProp(self, "config", { - configurable: true, - enumerable: true, - get: () => _config - }); - } + function _initDefaults() { + _config = null; + _pageAction = null; + _autoCaptureHandler = null; + _contentHandler = null; + _autoCapture = false; + } + }); } public initialize(config: IConfiguration & IConfig, core: IAppInsightsCore, extensions: IPlugin[], pluginChain?: ITelemetryPluginChain) { diff --git a/extensions/applicationinsights-properties-js/src/Context/User.ts b/extensions/applicationinsights-properties-js/src/Context/User.ts index a4d127e4f..5d9030d28 100644 --- a/extensions/applicationinsights-properties-js/src/Context/User.ts +++ b/extensions/applicationinsights-properties-js/src/Context/User.ts @@ -7,7 +7,7 @@ import { IAppInsightsCore, ICookieMgr, IUnloadHookContainer, _eInternalMessageId, _throwInternal, eLoggingSeverity, newId, onConfigChange, safeGetCookieMgr, safeGetLogger, toISOString } from "@microsoft/applicationinsights-core-js"; -import { objDefineProp } from "@nevware21/ts-utils"; +import { objDefine } from "@nevware21/ts-utils"; import { IPropertiesConfig } from "../Interfaces/IPropertiesConfig"; function _validateUserInput(id: string): boolean { @@ -76,10 +76,8 @@ export class User implements IUserContext { dynamicProto(User, this, (_self) => { // Define _self.config - objDefineProp(_self, "config", { - configurable: true, - enumerable: true, - get: () => config + objDefine(_self, "config", { + g: () => config }); let unloadHook = onConfigChange(config, () => { diff --git a/extensions/applicationinsights-properties-js/src/PropertiesPlugin.ts b/extensions/applicationinsights-properties-js/src/PropertiesPlugin.ts index bc837ed74..0bd73fa6f 100644 --- a/extensions/applicationinsights-properties-js/src/PropertiesPlugin.ts +++ b/extensions/applicationinsights-properties-js/src/PropertiesPlugin.ts @@ -12,7 +12,7 @@ import { IProcessTelemetryUnloadContext, ITelemetryItem, ITelemetryPluginChain, ITelemetryUnloadState, _InternalLogMessage, _eInternalMessageId, _logInternalMessage, createProcessTelemetryContext, eLoggingSeverity, getNavigator, getSetValue, isNullOrUndefined, onConfigChange } from "@microsoft/applicationinsights-core-js"; -import { objDeepFreeze, objDefineProp } from "@nevware21/ts-utils"; +import { objDeepFreeze, objDefine } from "@nevware21/ts-utils"; import { IPropTelemetryContext } from "./Interfaces/IPropTelemetryContext"; import { IPropertiesConfig } from "./Interfaces/IPropertiesConfig"; import { TelemetryContext } from "./TelemetryContext"; @@ -56,10 +56,8 @@ export default class PropertiesPlugin extends BaseTelemetryPlugin implements IPr _initDefaults(); - objDefineProp(_self, "context", { - enumerable: true, - configurable: true, - get: function() { + objDefine(_self, "context", { + g: function() { return _context; } }); diff --git a/shared/AppInsightsCommon/src/Interfaces/IChannelControlsAI.ts b/shared/AppInsightsCommon/src/Interfaces/IChannelControlsAI.ts deleted file mode 100644 index 3ee6d4fa6..000000000 --- a/shared/AppInsightsCommon/src/Interfaces/IChannelControlsAI.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { IChannelControls } from "@microsoft/applicationinsights-core-js"; - -export interface IChannelControlsAI extends IChannelControls { -} \ No newline at end of file diff --git a/shared/AppInsightsCommon/src/applicationinsights-common.ts b/shared/AppInsightsCommon/src/applicationinsights-common.ts index 02aa923f4..cabdb8de4 100644 --- a/shared/AppInsightsCommon/src/applicationinsights-common.ts +++ b/shared/AppInsightsCommon/src/applicationinsights-common.ts @@ -33,7 +33,6 @@ export { PageViewPerformance } from "./Telemetry/PageViewPerformance"; export { Data } from "./Telemetry/Common/Data"; export { eSeverityLevel, SeverityLevel } from "./Interfaces/Contracts/SeverityLevel"; export { IConfig, ConfigurationManager } from "./Interfaces/IConfig"; -export { IChannelControlsAI } from "./Interfaces/IChannelControlsAI"; export { IContextTagKeys, ContextTagKeys } from "./Interfaces/Contracts/ContextTagKeys"; export { DataSanitizerValues, diff --git a/shared/AppInsightsCore/Tests/Unit/src/Dynamic.Tests.ts b/shared/AppInsightsCore/Tests/Unit/src/Dynamic.Tests.ts index 8d1063ae3..319e96486 100644 --- a/shared/AppInsightsCore/Tests/Unit/src/Dynamic.Tests.ts +++ b/shared/AppInsightsCore/Tests/Unit/src/Dynamic.Tests.ts @@ -1,8 +1,8 @@ import { Assert, AITestClass } from "@microsoft/ai-test-framework"; +import { dumpObj } from "@nevware21/ts-utils"; import { AppInsightsCore } from "../../../src/JavaScriptSDK/AppInsightsCore"; -import { IChannelControls } from "../../../src/JavaScriptSDK.Interfaces/IChannelControls"; -import { _eInternalMessageId, LoggingSeverity } from "../../../src/JavaScriptSDK.Enums/LoggingEnums"; -import { _InternalLogMessage, DiagnosticLogger } from "../../../src/JavaScriptSDK/DiagnosticLogger"; +import { _eInternalMessageId } from "../../../src/JavaScriptSDK.Enums/LoggingEnums"; +import { _InternalLogMessage } from "../../../src/JavaScriptSDK/DiagnosticLogger"; import { IConfiguration } from "../../../src/JavaScriptSDK.Interfaces/IConfiguration"; import { IPlugin, ITelemetryPlugin } from "../../../src/JavaScriptSDK.Interfaces/ITelemetryPlugin"; import { ITelemetryItem } from "../../../src/JavaScriptSDK.Interfaces/ITelemetryItem"; @@ -10,9 +10,7 @@ import { BaseTelemetryPlugin } from "../../../src/JavaScriptSDK/BaseTelemetryPlu import { IAppInsightsCore } from "../../../src/JavaScriptSDK.Interfaces/IAppInsightsCore"; import { ITelemetryPluginChain } from "../../../src/JavaScriptSDK.Interfaces/ITelemetryPluginChain"; import { IProcessTelemetryContext } from "../../../src/JavaScriptSDK.Interfaces/IProcessTelemetryContext"; - -const AIInternalMessagePrefix = "AITR_"; -const MaxInt32 = 0xFFFFFFFF; +import { OldTrackPlugin, TestChannelPlugin, TestPlugin } from "./TestPlugins"; export class DynamicTests extends AITestClass { @@ -564,7 +562,7 @@ export class DynamicTests extends AITestClass { appInsightsCore.track({ name: "MyCustomEvent3" }); - Assert.equal(4, channelPlugin.events.length, "We should have a track call"); + Assert.equal(4, channelPlugin.events.length, "We should have a track call - " + dumpObj(channelPlugin.events)); Assert.equal(3, channelPlugin.events[3].data.trackPlugin); Assert.equal(true, channelPlugin.events[3].data.sampled); @@ -1201,75 +1199,6 @@ class TestSamplingPlugin implements ITelemetryPlugin { } } -class TestChannelPlugin implements IChannelControls { - public _nextPlugin: ITelemetryPlugin; - public isFlushInvoked = false; - public isUnloadInvoked = false; - public isTearDownInvoked = false; - public isResumeInvoked = false; - public isPauseInvoked = false; - public version: string = "1.0.33-Beta"; - - public processTelemetry; - - public identifier = "TestSender"; - - public priority: number = 1001; - public events: ITelemetryItem[] = []; - - constructor() { - this.processTelemetry = this._processTelemetry.bind(this); - } - public pause(): void { - this.isPauseInvoked = true; - } - - public resume(): void { - this.isResumeInvoked = true; - } - - public teardown(): void { - this.isTearDownInvoked = true; - } - - flush(async?: boolean, callBack?: () => void): void { - this.isFlushInvoked = true; - if (callBack) { - callBack(); - } - } - - onunloadFlush(async?: boolean) { - this.isUnloadInvoked = true; - } - - setNextPlugin(next: ITelemetryPlugin) { - this._nextPlugin = next; - } - - public initialize = (config: IConfiguration) => { - } - - public _processTelemetry(env: ITelemetryItem) { - this.events.push(env); - - // Just calling processTelemetry as this is the original design of the Plugins (as opposed to the newer processNext()) - this._nextPlugin?.processTelemetry(env); - } -} - -class TestPlugin implements IPlugin { - public identifier: string = "TestPlugin"; - public version: string = "1.0.31-Beta"; - - private _config: IConfiguration; - - public initialize(config: IConfiguration) { - this._config = config; - // do custom one time initialization - } -} - class TrackPlugin extends BaseTelemetryPlugin { public identifier: string = "TrackPlugin"; public version: string = "1.0.31-Beta"; @@ -1290,22 +1219,3 @@ class TrackPlugin extends BaseTelemetryPlugin { itemCtx.processNext(evt); } } - -class OldTrackPlugin implements ITelemetryPlugin { - public identifier: string = "OldTrackPlugin"; - public priority = 2; - public isInitialized: any; - private _config: IConfiguration; - public index: number = 0; - - public initialize(config: IConfiguration, core: IAppInsightsCore, extensions: IPlugin[], pluginChain?: ITelemetryPluginChain) { - this._config = config; - core.track({ name: 'TestEvent1' }); - } - - public processTelemetry(evt: ITelemetryItem, itemCtx: IProcessTelemetryContext) { - let data = evt.data = (evt.data || {}); - data.trackPlugin = this.index++; - itemCtx.processNext(evt); - } -} diff --git a/shared/AppInsightsCore/Tests/Unit/src/DynamicConfig.Tests.ts b/shared/AppInsightsCore/Tests/Unit/src/DynamicConfig.Tests.ts index 8f1b3e056..3d8fc988f 100644 --- a/shared/AppInsightsCore/Tests/Unit/src/DynamicConfig.Tests.ts +++ b/shared/AppInsightsCore/Tests/Unit/src/DynamicConfig.Tests.ts @@ -14,6 +14,7 @@ import { ITelemetryItem } from "../../../src/JavaScriptSDK.Interfaces/ITelemetry import { ITelemetryPluginChain } from "../../../src/JavaScriptSDK.Interfaces/ITelemetryPluginChain"; import { ITelemetryPlugin } from "../../../src/JavaScriptSDK.Interfaces/ITelemetryPlugin"; import { IChannelControls } from "../../../src/JavaScriptSDK.Interfaces/IChannelControls"; +import { TestPlugin, TestSamplingPlugin, TrackPlugin } from "./TestPlugins"; type NoRepeats = { [M in keyof T]: { [N in keyof T]: N extends M ? never : T[M] extends T[N] ? unknown : never @@ -1046,6 +1047,55 @@ export class DynamicConfigTests extends AITestClass { } }); + this.testCase({ + name: "Direct Updating of readonly fields", + test: () => { + const iKey1 = "09465199-12AA-4124-817F-544738CC7C41"; + const iKey2 = "00000000-1111-7777-8888-999999999999"; + const testEndpoint1 = "https://localhost:9001/TestEndpoint"; + + const channelPlugin = new TestChannelPlugin(); + const trackPlugin = new TrackPlugin(); + const appInsightsCore = new AppInsightsCore(); + const testSamplingPlugin = new TestSamplingPlugin(); + + const config: IConfiguration = { + instrumentationKey: iKey1 + }; + try { + appInsightsCore.initialize(config, [channelPlugin, new TestPlugin(), trackPlugin, testSamplingPlugin]); + } catch (error) { + Assert.ok(false, "Everything should be initialized"); + } + + Assert.equal(1, channelPlugin.events.length, "We should have a track call"); + Assert.equal(0, channelPlugin.events[0].data.trackPlugin); + Assert.equal(true, channelPlugin.events[0].data.sampled); + + Assert.equal(iKey1, appInsightsCore.config.instrumentationKey, "Test Core Instrumentation Key"); + Assert.equal(undefined, appInsightsCore.config.endpointUrl, "Test Core Endpoint 1"); + Assert.equal(true, appInsightsCore.getCookieMgr().isEnabled(), "Cookie Manager should be enabled"); + Assert.equal(0, appInsightsCore.logger.consoleLoggingLevel(), "Validate the Console Logging Level") + + Assert.equal(iKey1, trackPlugin._config.instrumentationKey, "Test plugin Instrumentation Key"); + Assert.equal(undefined, trackPlugin._config.endpointUrl, "Test plugin Endpoint 1"); + + Assert.equal(undefined, testSamplingPlugin._updatedConfig, "Config has not been updated"); + + Assert.equal(4, appInsightsCore.config.extensions!.length, dumpObj(appInsightsCore.config.extensions)); + + _expectException(() => { + // casting to any to bypass TypeScript readonly error + (appInsightsCore.config as any).extensions = [] + }, "We should not be able to update the extensions directly"); + + _expectException(() => { + // casting to any to bypass TypeScript readonly error + (appInsightsCore.config as any).channels = [[]] + }, "We should not be able to update the channels directly"); + } + }); + function createPerfMgr(core: IAppInsightsCore, manager: INotificationManager): IPerfManager { return createPerfMgr(core, manager); } diff --git a/shared/AppInsightsCore/Tests/Unit/src/EventHelper.Tests.ts b/shared/AppInsightsCore/Tests/Unit/src/EventHelper.Tests.ts index 6010ed23a..879809b69 100644 --- a/shared/AppInsightsCore/Tests/Unit/src/EventHelper.Tests.ts +++ b/shared/AppInsightsCore/Tests/Unit/src/EventHelper.Tests.ts @@ -1,7 +1,7 @@ import { Assert, AITestClass } from "@microsoft/ai-test-framework"; -import { addEventHandler, addEventListeners, createUniqueNamespace, removeEventHandler } from "../../../src/applicationinsights-core-js"; -import { _eInternalMessageId, LoggingSeverity } from "../../../src/JavaScriptSDK.Enums/LoggingEnums"; -import { _InternalLogMessage, DiagnosticLogger } from "../../../src/JavaScriptSDK/DiagnosticLogger"; +import { addEventHandler, createUniqueNamespace, removeEventHandler } from "../../../src/applicationinsights-core-js"; +import { _eInternalMessageId } from "../../../src/JavaScriptSDK.Enums/LoggingEnums"; +import { _InternalLogMessage } from "../../../src/JavaScriptSDK/DiagnosticLogger"; import { mergeEvtNamespace, __getRegisteredEvents } from "../../../src/JavaScriptSDK/EventHelpers"; export class EventHelperTests extends AITestClass { diff --git a/shared/AppInsightsCore/Tests/Unit/src/TestPlugins.ts b/shared/AppInsightsCore/Tests/Unit/src/TestPlugins.ts new file mode 100644 index 000000000..fcd4b17ab --- /dev/null +++ b/shared/AppInsightsCore/Tests/Unit/src/TestPlugins.ts @@ -0,0 +1,197 @@ +import { Assert, AITestClass } from "@microsoft/ai-test-framework"; +import { _eInternalMessageId } from "../../../src/JavaScriptSDK.Enums/LoggingEnums"; +import { _InternalLogMessage } from "../../../src/JavaScriptSDK/DiagnosticLogger"; +import { ITelemetryItem } from "../../../src/JavaScriptSDK.Interfaces/ITelemetryItem"; +import { IProcessTelemetryContext, IProcessTelemetryUpdateContext } from "../../../src/JavaScriptSDK.Interfaces/IProcessTelemetryContext"; +import { TelemetryUpdateReason } from "../../../types/applicationinsights-core-js"; +import { IConfiguration } from "../../../src/JavaScriptSDK.Interfaces/IConfiguration"; +import { IPlugin, ITelemetryPlugin } from "../../../src/JavaScriptSDK.Interfaces/ITelemetryPlugin"; +import { BaseTelemetryPlugin } from "../../../src/JavaScriptSDK/BaseTelemetryPlugin"; +import { IAppInsightsCore } from "../../../src/JavaScriptSDK.Interfaces/IAppInsightsCore"; +import { ITelemetryPluginChain } from "../../../src/JavaScriptSDK.Interfaces/ITelemetryPluginChain"; +import { ITelemetryUpdateState } from "../../../src/JavaScriptSDK.Interfaces/ITelemetryUpdateState"; +import { IChannelControls } from "../../../src/JavaScriptSDK.Interfaces/IChannelControls"; + + +export class TestPlugin implements IPlugin { + public identifier: string = "TestPlugin"; + public version: string = "1.0.31-Beta"; + + public _config: IConfiguration; + + public initialize(config: IConfiguration) { + this._config = config; + // do custom one time initialization + } +} + +export class TrackPlugin extends BaseTelemetryPlugin { + public identifier: string = "TrackPlugin"; + public version: string = "1.0.31-Beta"; + public priority = 2; + public isInitialized: any; + public _config: IConfiguration; + public index: number = 0; + + public initialize(config: IConfiguration, core: IAppInsightsCore, extensions: IPlugin[], pluginChain?: ITelemetryPluginChain) { + super.initialize(config, core, extensions, pluginChain) + this._config = config; + core.track({ name: 'TestEvent1' }); + } + + public processTelemetry(evt: ITelemetryItem, itemCtx: IProcessTelemetryContext) { + let data = evt.data = (evt.data || {}); + data.trackPlugin = this.index++; + itemCtx.processNext(evt); + } + + protected _doUpdate = (updateCtx?: IProcessTelemetryUpdateContext, updateState?: ITelemetryUpdateState, asyncCallback?: () => void) => { + if (updateState.reason & TelemetryUpdateReason.ConfigurationChanged) { + this._config = updateState.cfg; + } + } +} + +export class OldTrackPlugin implements ITelemetryPlugin { + public identifier: string = "OldTrackPlugin"; + public priority = 2; + public isInitialized: any; + public _config: IConfiguration; + public index: number = 0; + + public initialize(config: IConfiguration, core: IAppInsightsCore, extensions: IPlugin[], pluginChain?: ITelemetryPluginChain) { + this._config = config; + core.track({ name: 'TestEvent1' }); + } + + public processTelemetry(evt: ITelemetryItem, itemCtx: IProcessTelemetryContext) { + let data = evt.data = (evt.data || {}); + data.trackPlugin = this.index++; + itemCtx.processNext(evt); + } +} + +/** + * Test plugin doesn't implement the teardown "unload" function + */ +export class TestSamplingPlugin implements ITelemetryPlugin { + public processTelemetry: (env: ITelemetryItem) => void; + public initialize: (config: IConfiguration) => void; + public identifier: string = "AzureSamplingPlugin"; + public setNextPlugin: (next: ITelemetryPlugin) => void; + public priority: number = 5; + public version = "1.0.31-Beta"; + public nextPlugin: ITelemetryPlugin; + public isSampledOut: boolean = false; + public teardownCalled: boolean = false; + public _updatedConfig: IConfiguration; + private _validateItem = false; + + constructor(validateItem: boolean = false) { + this.processTelemetry = this._processTelemetry.bind(this); + this.initialize = this._start.bind(this); + this.setNextPlugin = this._setNextPlugin.bind(this); + this._validateItem = validateItem; + } + + public teardown() { + this.teardownCalled = true; + } + + public update(updateCtx: IProcessTelemetryUpdateContext, updateState: ITelemetryUpdateState) { + if (updateState.reason & TelemetryUpdateReason.ConfigurationChanged) { + this._updatedConfig = updateState.cfg; + } + } + + private _processTelemetry(env: ITelemetryItem) { + if (!env) { + throw Error("Invalid telemetry object"); + } + + if (this._validateItem) { + Assert.ok(env.baseData); + Assert.ok(env.baseType); + Assert.ok(env.data); + Assert.ok(env.ext); + Assert.ok(env.tags); + } + + let data = env.data = (env.data || {}); + data.sampled = true; + + if (!this.isSampledOut) { + this.nextPlugin?.processTelemetry(env); + } + } + + private _start(config: IConfiguration) { + if (!config) { + throw Error("required configuration missing"); + } + + const pluginConfig = config.extensions ? config.extensions[this.identifier] : null; + this.isSampledOut = pluginConfig ? pluginConfig.isSampledOut : false; + } + + private _setNextPlugin(next: ITelemetryPlugin): void { + this.nextPlugin = next; + } +} + +export class TestChannelPlugin implements IChannelControls { + public _nextPlugin: ITelemetryPlugin; + public isFlushInvoked = false; + public isUnloadInvoked = false; + public isTearDownInvoked = false; + public isResumeInvoked = false; + public isPauseInvoked = false; + public version: string = "1.0.33-Beta"; + + public processTelemetry; + + public identifier = "TestSender"; + + public priority: number = 1001; + public events: ITelemetryItem[] = []; + + constructor() { + this.processTelemetry = this._processTelemetry.bind(this); + } + public pause(): void { + this.isPauseInvoked = true; + } + + public resume(): void { + this.isResumeInvoked = true; + } + + public teardown(): void { + this.isTearDownInvoked = true; + } + + flush(async?: boolean, callBack?: () => void): void { + this.isFlushInvoked = true; + if (callBack) { + callBack(); + } + } + + onunloadFlush(async?: boolean) { + this.isUnloadInvoked = true; + } + + setNextPlugin(next: ITelemetryPlugin) { + this._nextPlugin = next; + } + + public initialize = (config: IConfiguration) => { + } + + public _processTelemetry(env: ITelemetryItem) { + this.events.push(env); + + // Just calling processTelemetry as this is the original design of the Plugins (as opposed to the newer processNext()) + this._nextPlugin?.processTelemetry(env); + } +} diff --git a/shared/AppInsightsCore/Tests/Unit/src/UpdateConfig.Tests.ts b/shared/AppInsightsCore/Tests/Unit/src/UpdateConfig.Tests.ts index 7686e212b..45603fb90 100644 --- a/shared/AppInsightsCore/Tests/Unit/src/UpdateConfig.Tests.ts +++ b/shared/AppInsightsCore/Tests/Unit/src/UpdateConfig.Tests.ts @@ -1,17 +1,10 @@ import { Assert, AITestClass } from "@microsoft/ai-test-framework"; +import { dumpObj } from "@nevware21/ts-utils"; import { AppInsightsCore } from "../../../src/JavaScriptSDK/AppInsightsCore"; -import { IChannelControls } from "../../../src/JavaScriptSDK.Interfaces/IChannelControls"; import { _eInternalMessageId } from "../../../src/JavaScriptSDK.Enums/LoggingEnums"; import { _InternalLogMessage } from "../../../src/JavaScriptSDK/DiagnosticLogger"; import { IConfiguration } from "../../../src/JavaScriptSDK.Interfaces/IConfiguration"; -import { IPlugin, ITelemetryPlugin } from "../../../src/JavaScriptSDK.Interfaces/ITelemetryPlugin"; -import { ITelemetryItem } from "../../../src/JavaScriptSDK.Interfaces/ITelemetryItem"; -import { BaseTelemetryPlugin } from "../../../src/JavaScriptSDK/BaseTelemetryPlugin"; -import { IAppInsightsCore } from "../../../src/JavaScriptSDK.Interfaces/IAppInsightsCore"; -import { ITelemetryPluginChain } from "../../../src/JavaScriptSDK.Interfaces/ITelemetryPluginChain"; -import { IProcessTelemetryContext, IProcessTelemetryUpdateContext } from "../../../src/JavaScriptSDK.Interfaces/IProcessTelemetryContext"; -import { ITelemetryUpdateState } from "../../../src/applicationinsights-core-js"; -import { TelemetryUpdateReason } from "../../../types/applicationinsights-core-js"; +import { OldTrackPlugin, TestChannelPlugin, TestPlugin, TestSamplingPlugin, TrackPlugin } from "./TestPlugins"; const AIInternalMessagePrefix = "AITR_"; const MaxInt32 = 0xFFFFFFFF; @@ -65,7 +58,8 @@ export class UpdateConfigTests extends AITestClass { appInsightsCore.updateCfg({ endpointUrl: "https://localhost:9001/TestEndpoint", - disableCookiesUsage: true + disableCookiesUsage: true, + extensions: [] // Try and replace the extensions }, true); Assert.equal(iKey1, appInsightsCore.config.instrumentationKey, "Test Core Instrumentation Key"); @@ -79,6 +73,7 @@ export class UpdateConfigTests extends AITestClass { Assert.equal(appInsightsCore.config, testSamplingPlugin._updatedConfig, "Config has been updated to the same as the core"); Assert.equal(iKey1, testSamplingPlugin._updatedConfig.instrumentationKey, "Test sampling Instrumentation Key"); Assert.equal(testEndpoint1, testSamplingPlugin._updatedConfig.endpointUrl, "Test sampling Endpoint 1"); + Assert.equal(4, appInsightsCore.config.extensions!.length, dumpObj(appInsightsCore.config.extensions)); appInsightsCore.updateCfg({ instrumentationKey: iKey2, @@ -275,186 +270,3 @@ export class UpdateConfigTests extends AITestClass { }); } } - -/** - * Test plugin doesn't implement the teardown "unload" function - */ -class TestSamplingPlugin implements ITelemetryPlugin { - public processTelemetry: (env: ITelemetryItem) => void; - public initialize: (config: IConfiguration) => void; - public identifier: string = "AzureSamplingPlugin"; - public setNextPlugin: (next: ITelemetryPlugin) => void; - public priority: number = 5; - public version = "1.0.31-Beta"; - public nextPlugin: ITelemetryPlugin; - public isSampledOut: boolean = false; - public teardownCalled: boolean = false; - public _updatedConfig: IConfiguration; - private _validateItem = false; - - constructor(validateItem: boolean = false) { - this.processTelemetry = this._processTelemetry.bind(this); - this.initialize = this._start.bind(this); - this.setNextPlugin = this._setNextPlugin.bind(this); - this._validateItem = validateItem; - } - - public teardown() { - this.teardownCalled = true; - } - - public update(updateCtx: IProcessTelemetryUpdateContext, updateState: ITelemetryUpdateState) { - if (updateState.reason & TelemetryUpdateReason.ConfigurationChanged) { - this._updatedConfig = updateState.cfg; - } - } - - private _processTelemetry(env: ITelemetryItem) { - if (!env) { - throw Error("Invalid telemetry object"); - } - - if (this._validateItem) { - Assert.ok(env.baseData); - Assert.ok(env.baseType); - Assert.ok(env.data); - Assert.ok(env.ext); - Assert.ok(env.tags); - } - - let data = env.data = (env.data || {}); - data.sampled = true; - - if (!this.isSampledOut) { - this.nextPlugin?.processTelemetry(env); - } - } - - private _start(config: IConfiguration) { - if (!config) { - throw Error("required configuration missing"); - } - - const pluginConfig = config.extensions ? config.extensions[this.identifier] : null; - this.isSampledOut = pluginConfig ? pluginConfig.isSampledOut : false; - } - - private _setNextPlugin(next: ITelemetryPlugin): void { - this.nextPlugin = next; - } -} - -class TestChannelPlugin implements IChannelControls { - public _nextPlugin: ITelemetryPlugin; - public isFlushInvoked = false; - public isUnloadInvoked = false; - public isTearDownInvoked = false; - public isResumeInvoked = false; - public isPauseInvoked = false; - public version: string = "1.0.33-Beta"; - - public processTelemetry; - - public identifier = "TestSender"; - - public priority: number = 1001; - public events: ITelemetryItem[] = []; - - constructor() { - this.processTelemetry = this._processTelemetry.bind(this); - } - public pause(): void { - this.isPauseInvoked = true; - } - - public resume(): void { - this.isResumeInvoked = true; - } - - public teardown(): void { - this.isTearDownInvoked = true; - } - - flush(async?: boolean, callBack?: () => void): void { - this.isFlushInvoked = true; - if (callBack) { - callBack(); - } - } - - onunloadFlush(async?: boolean) { - this.isUnloadInvoked = true; - } - - setNextPlugin(next: ITelemetryPlugin) { - this._nextPlugin = next; - } - - public initialize = (config: IConfiguration) => { - } - - public _processTelemetry(env: ITelemetryItem) { - this.events.push(env); - - // Just calling processTelemetry as this is the original design of the Plugins (as opposed to the newer processNext()) - this._nextPlugin?.processTelemetry(env); - } -} - -class TestPlugin implements IPlugin { - public identifier: string = "TestPlugin"; - public version: string = "1.0.31-Beta"; - - public _config: IConfiguration; - - public initialize(config: IConfiguration) { - this._config = config; - // do custom one time initialization - } -} - -class TrackPlugin extends BaseTelemetryPlugin { - public identifier: string = "TrackPlugin"; - public version: string = "1.0.31-Beta"; - public priority = 2; - public isInitialized: any; - public _config: IConfiguration; - public index: number = 0; - - public initialize(config: IConfiguration, core: IAppInsightsCore, extensions: IPlugin[], pluginChain?: ITelemetryPluginChain) { - super.initialize(config, core, extensions, pluginChain) - this._config = config; - core.track({ name: 'TestEvent1' }); - } - - public processTelemetry(evt: ITelemetryItem, itemCtx: IProcessTelemetryContext) { - let data = evt.data = (evt.data || {}); - data.trackPlugin = this.index++; - itemCtx.processNext(evt); - } - - protected _doUpdate = (updateCtx?: IProcessTelemetryUpdateContext, updateState?: ITelemetryUpdateState, asyncCallback?: () => void) => { - if (updateState.reason & TelemetryUpdateReason.ConfigurationChanged) { - this._config = updateState.cfg; - } - } -} - -class OldTrackPlugin implements ITelemetryPlugin { - public identifier: string = "OldTrackPlugin"; - public priority = 2; - public isInitialized: any; - public _config: IConfiguration; - public index: number = 0; - - public initialize(config: IConfiguration, core: IAppInsightsCore, extensions: IPlugin[], pluginChain?: ITelemetryPluginChain) { - this._config = config; - core.track({ name: 'TestEvent1' }); - } - - public processTelemetry(evt: ITelemetryItem, itemCtx: IProcessTelemetryContext) { - let data = evt.data = (evt.data || {}); - data.trackPlugin = this.index++; - itemCtx.processNext(evt); - } -} diff --git a/shared/AppInsightsCore/src/Config/DynamicConfig.ts b/shared/AppInsightsCore/src/Config/DynamicConfig.ts index e1e1297d9..ce8296e6e 100644 --- a/shared/AppInsightsCore/src/Config/DynamicConfig.ts +++ b/shared/AppInsightsCore/src/Config/DynamicConfig.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { dumpObj, objDefineProp, objForEachKey } from "@nevware21/ts-utils"; +import { dumpObj, objDefine, objForEachKey } from "@nevware21/ts-utils"; import { _eInternalMessageId, eLoggingSeverity } from "../JavaScriptSDK.Enums/LoggingEnums"; import { IConfiguration } from "../JavaScriptSDK.Interfaces/IConfiguration"; import { IDiagnosticLogger } from "../JavaScriptSDK.Interfaces/IDiagnosticLogger"; @@ -108,11 +108,11 @@ function _createDynamicHandler(logger: IDiagnosticLogg _block: _block }; - objDefineProp(cfgHandler, "uid", { - configurable: false, - enumerable: false, - writable: false, - value: uid + objDefine(cfgHandler, "uid", { + c: false, + e: false, + w: false, + v: uid }); theState = _createState(cfgHandler); diff --git a/shared/AppInsightsCore/src/Config/DynamicProperty.ts b/shared/AppInsightsCore/src/Config/DynamicProperty.ts index d7fc8eb07..1c2a5a128 100644 --- a/shared/AppInsightsCore/src/Config/DynamicProperty.ts +++ b/shared/AppInsightsCore/src/Config/DynamicProperty.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import { - arrForEach, arrIndexOf, dumpObj, isArray, isPlainObject, objDefineAccessors, objDefineProp, objForEachKey, objGetOwnPropertyDescriptor + arrForEach, arrIndexOf, dumpObj, isArray, isPlainObject, objDefine, objDefineProp, objForEachKey, objGetOwnPropertyDescriptor } from "@nevware21/ts-utils"; import { UNDEFINED_VALUE } from "../JavaScriptSDK/InternalConstants"; import { CFG_HANDLER_LINK, throwInvalidAccess } from "./DynamicSupport"; @@ -160,7 +160,7 @@ function _makeDynamicProperty(state: _IDynamicConfigHandlerState< } } - objDefineAccessors(theConfig, detail.n, _getProperty, _setProperty, true); + objDefine(theConfig, detail.n, { g: _getProperty, s: _setProperty }); // Return the dynamic reference return _getProperty(); @@ -205,8 +205,6 @@ export function _makeDynamicObject(state: _IDynamicConfigHandlerState, tar if (!target[CFG_HANDLER_LINK]) { // Link the config back to the dynamic config details objDefineProp(target, CFG_HANDLER_LINK, { - configurable: false, - enumerable: false, get: function() { return state.hdlr; } diff --git a/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IChannelControlsHost.ts b/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IChannelControlsHost.ts new file mode 100644 index 000000000..b5ba30cc7 --- /dev/null +++ b/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IChannelControlsHost.ts @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { IChannelControls } from "./IChannelControls"; +import { IPlugin } from "./ITelemetryPlugin"; + +export interface IChannelControlsHost extends IChannelControls { + /** + * Get and return the named channel instance (if present) from the queues + * @param pluginIdentifier - The identifier name of the plugin + */ + getChannel(pluginIdentifier: string): T; +} \ No newline at end of file diff --git a/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IConfiguration.ts b/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IConfiguration.ts index a40c27298..e438326d9 100644 --- a/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IConfiguration.ts +++ b/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IConfiguration.ts @@ -77,13 +77,13 @@ export interface IConfiguration { /** * Additional plugins that should be loaded by core at runtime */ - extensions?: ITelemetryPlugin[]; + readonly extensions?: ITelemetryPlugin[]; /** * Channel queues that is setup by caller in desired order. * If channels are provided here, core will ignore any channels that are already setup, example if there is a SKU with an initialized channel */ - channels?: IChannelControls[][]; + readonly channels?: IChannelControls[][]; /** * @type {boolean} diff --git a/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/ITelemetryPlugin.ts b/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/ITelemetryPlugin.ts index a8cde3d82..9af8f1179 100644 --- a/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/ITelemetryPlugin.ts +++ b/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/ITelemetryPlugin.ts @@ -41,7 +41,7 @@ export interface ITelemetryPlugin extends ITelemetryProcessor, IPlugin { * now for backward compatibility only. */ setNextPlugin?: (next: ITelemetryPlugin | ITelemetryPluginChain) => void; - + /** * Priority of the extension */ diff --git a/shared/AppInsightsCore/src/JavaScriptSDK/AppInsightsCore.ts b/shared/AppInsightsCore/src/JavaScriptSDK/AppInsightsCore.ts index f268e3ba4..544b78de2 100644 --- a/shared/AppInsightsCore/src/JavaScriptSDK/AppInsightsCore.ts +++ b/shared/AppInsightsCore/src/JavaScriptSDK/AppInsightsCore.ts @@ -5,7 +5,7 @@ import dynamicProto from "@microsoft/dynamicproto-js"; import { ITimerHandler, arrAppend, arrForEach, arrIndexOf, deepExtend, hasDocument, isFunction, isNullOrUndefined, isPlainObject, objDeepFreeze, - objDefineProp, objForEachKey, objFreeze, objHasOwn, scheduleInterval, scheduleTimeout, throwError + objDefine, objForEachKey, objFreeze, objHasOwn, scheduleInterval, scheduleTimeout, throwError } from "@nevware21/ts-utils"; import { createDynamicConfig, onConfigChange } from "../Config/DynamicConfig"; import { IConfigDefaults } from "../Config/IConfigDefaults"; @@ -18,6 +18,7 @@ import { TelemetryUnloadReason } from "../JavaScriptSDK.Enums/TelemetryUnloadRea import { TelemetryUpdateReason } from "../JavaScriptSDK.Enums/TelemetryUpdateReason"; import { IAppInsightsCore, ILoadedPlugin } from "../JavaScriptSDK.Interfaces/IAppInsightsCore"; import { IChannelControls } from "../JavaScriptSDK.Interfaces/IChannelControls"; +import { IChannelControlsHost } from "../JavaScriptSDK.Interfaces/IChannelControlsHost"; import { IConfiguration } from "../JavaScriptSDK.Interfaces/IConfiguration"; import { ICookieMgr } from "../JavaScriptSDK.Interfaces/ICookieMgr"; import { IDiagnosticLogger } from "../JavaScriptSDK.Interfaces/IDiagnosticLogger"; @@ -65,9 +66,9 @@ const strSdkNotInitialized = "SDK is not initialized"; */ const defaultConfig: IConfigDefaults = objDeepFreeze({ cookieCfg: {}, - [STR_EXTENSIONS]: [], - [STR_CHANNELS]: [], - [STR_EXTENSION_CONFIG]: {}, + [STR_EXTENSIONS]: { rdOnly: true, ref: true, v: [] }, + [STR_CHANNELS]: { rdOnly: true, ref: true, v:[] }, + [STR_EXTENSION_CONFIG]: { ref: true, v: {} }, [STR_CREATE_PERF_MGR]: UNDEFINED_VALUE, loggingLevelConsole: eLoggingSeverity.DISABLED, diagnosticLogInterval: UNDEFINED_VALUE @@ -627,12 +628,12 @@ export class AppInsightsCore im let cfg = _configHandler.cfg; // replace the immutable (if initialized) values - newConfig.extensions = cfg.extensions; - newConfig.channels = cfg.channels; + // We don't currently allow updating the extensions and channels via the update config + // So overwriting any user provided values to reuse the existing values + (newConfig as any).extensions = cfg.extensions; + (newConfig as any).channels = cfg.channels; } - // We don't currently allow updating the extensions and channels via the update config - // So overwriting any user provided values to reuse the existing values // Explicitly blocking any previous config watchers so that they don't get called because // of this bulk update (Probably not necessary) (_configHandler as _IInternalDynamicConfigHandler)._block((details) => { @@ -741,19 +742,15 @@ export class AppInsightsCore im _configHandler.cfg.loggingLevelConsole = eLoggingSeverity.CRITICAL; // Define _self.config - objDefineProp(_self, "config", { - configurable: true, - enumerable: true, - get: () => _configHandler.cfg, - set: (newValue) => { + objDefine(_self, "config", { + g: () => _configHandler.cfg, + s: (newValue) => { _self.updateCfg(newValue, false); } }); - objDefineProp(_self, "pluginVersionStringArr", { - configurable: true, - enumerable: true, - get: () => { + objDefine(_self, "pluginVersionStringArr", { + g: () => { if (!_pluginVersionStringArr) { _setPluginVersions(); } @@ -762,10 +759,8 @@ export class AppInsightsCore im } }); - objDefineProp(_self, "pluginVersionString", { - configurable: true, - enumerable: true, - get: () => { + objDefine(_self, "pluginVersionString", { + g: () => { if (!_pluginVersionString) { if (!_pluginVersionStringArr) { _setPluginVersions(); @@ -780,6 +775,9 @@ export class AppInsightsCore im _self.logger = new DiagnosticLogger(_configHandler.cfg); _extensions = []; + let cfgExtensions = _self.config.extensions || []; + cfgExtensions.splice(0, cfgExtensions.length); + arrAppend(cfgExtensions, _extensions); _telemetryInitializerPlugin = new TelemetryInitializerPlugin(); _eventQueue = []; @@ -791,6 +789,7 @@ export class AppInsightsCore im _configExtensions = []; _channelConfig = null; _channels = null; + _isUnloading = false; _internalLogsEventName = null; _evtNamespace = createUniqueNamespace("AIBaseCore", true); @@ -828,6 +827,13 @@ export class AppInsightsCore im // Required to allow plugins to call core.getPlugin() during their own initialization _extensions = objFreeze(allExtensions); + // This has a side effect of adding the extensions passed during initialization + // into the config.extensions, so you can see all of the extensions loaded. + // This will also get updated by the addPlugin() and remove plugin code. + let cfgExtensions = _self.config.extensions || []; + cfgExtensions.splice(0, cfgExtensions.length); + arrAppend(cfgExtensions, _extensions); + let rootCtx = _createTelCtx(); // Initializing the channels first @@ -846,6 +852,7 @@ export class AppInsightsCore im function _getPlugin(pluginIdentifier: string): ILoadedPlugin { let theExt: ILoadedPlugin = null; let thePlugin: IPlugin = null; + let channelHosts: IChannelControlsHost[] = []; arrForEach(_extensions, (ext: any) => { if (ext.identifier === pluginIdentifier && ext !== _telemetryInitializerPlugin) { @@ -853,14 +860,19 @@ export class AppInsightsCore im return -1; } - // TODO: Check if the extension is an extension "host" (like the TeeChannel) - // So that if the extension is not found we can ask the "host" plugins for the plugin + if ((ext as IChannelControlsHost).getChannel) { + channelHosts.push(ext as IChannelControlsHost); + } }); - // if (!thePlugin && _channelControl) { - // // Check the channel Controller - // thePlugin = _channelControl.getChannel(pluginIdentifier); - // } + if (!thePlugin && channelHosts.length > 0) { + arrForEach(channelHosts, (host) => { + thePlugin = host.getChannel(pluginIdentifier); + if (!thePlugin) { + return -1; + } + }); + } if (thePlugin) { theExt = { diff --git a/shared/AppInsightsCore/src/JavaScriptSDK/DataCacheHelper.ts b/shared/AppInsightsCore/src/JavaScriptSDK/DataCacheHelper.ts index fb3d61845..79f2e2bac 100644 --- a/shared/AppInsightsCore/src/JavaScriptSDK/DataCacheHelper.ts +++ b/shared/AppInsightsCore/src/JavaScriptSDK/DataCacheHelper.ts @@ -1,13 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { ObjDefineProperty } from "@microsoft/applicationinsights-shims"; +import { objDefine } from "@nevware21/ts-utils"; import { normalizeJsName } from "./HelperFuncs"; import { STR_EMPTY } from "./InternalConstants"; import { newId } from "./RandomHelper"; -const _objDefineProperty = ObjDefineProperty; - const version = "#version#"; let instanceName = "." + newId(6); let _dataUid = 0; @@ -19,24 +17,6 @@ export interface IDataCache { kill: (target: any, name: string) => void; } -function _createAccessor(target: any, prop: string, value: T): boolean { - if (_objDefineProperty) { - try { - _objDefineProperty(target, prop, { - value: value, - enumerable: false, - configurable: true - }); - return true; - } catch (e) { - // IE8 Defines a defineProperty on Object but it's only supported for DOM elements so it will throw - // We will just ignore this here. - } - } - - return false; -} - // Accepts only: // - Node // - Node.ELEMENT_NODE @@ -54,10 +34,10 @@ function _getCache(data: IDataCache, target: Node) { try { if (_canAcceptData(target)) { - if (!_createAccessor(target, data.id, theCache)) { - // Environment doesn't support accessor, so just use direct assignment - target[data.id] = theCache; - } + objDefine(target, data.id, { + e: false, + v: theCache + }); } } catch (e) { // Not all environments allow extending all objects, so just ignore the cache in those cases diff --git a/shared/AppInsightsCore/src/JavaScriptSDK/HelperFuncs.ts b/shared/AppInsightsCore/src/JavaScriptSDK/HelperFuncs.ts index bcb73c790..4fb5b7bfa 100644 --- a/shared/AppInsightsCore/src/JavaScriptSDK/HelperFuncs.ts +++ b/shared/AppInsightsCore/src/JavaScriptSDK/HelperFuncs.ts @@ -3,7 +3,7 @@ import { ObjAssign, ObjClass } from "@microsoft/applicationinsights-shims"; import { arrForEach, asString as asString21, isArray, isBoolean, isError, isFunction, isNullOrUndefined, isObject, isPlainObject, isString, - isUndefined, objDeepFreeze, objDefineAccessors, objForEachKey, objHasOwn, strIndexOf + isUndefined, objDeepFreeze, objDefine, objForEachKey, objHasOwn, strIndexOf } from "@nevware21/ts-utils"; import { STR_EMPTY } from "./InternalConstants"; @@ -184,16 +184,14 @@ export function proxyAssign(target: T, source: S, chkSet?: (name: string, delete (target as any)[field]; } - if (!objDefineAccessors(target, field, () => { - return source[field]; - }, (theValue) => { - source[field] = theValue; - })) { - // Unable to create an accessor, so just assign the values as a fallback - // -- this will (mostly) work for objects - // -- but will fail for accessing primitives (if the source changes it) and all types of "setters" as the source won't be modified - target[field as string] = value; - } + objDefine(target, field, { + g: () => { + return source[field]; + }, + s: (theValue) => { + source[field] = theValue; + } + }); } } } diff --git a/shared/AppInsightsCore/src/JavaScriptSDK/PerfManager.ts b/shared/AppInsightsCore/src/JavaScriptSDK/PerfManager.ts index 80c465f4e..f04e8f18c 100644 --- a/shared/AppInsightsCore/src/JavaScriptSDK/PerfManager.ts +++ b/shared/AppInsightsCore/src/JavaScriptSDK/PerfManager.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. import dynamicProto from "@microsoft/dynamicproto-js"; -import { isArray, isFunction, objDefineAccessors, utcNow } from "@nevware21/ts-utils"; +import { isArray, isFunction, objDefine, utcNow } from "@nevware21/ts-utils"; import { INotificationManager } from "../JavaScriptSDK.Interfaces/INotificationManager"; import { IPerfEvent } from "../JavaScriptSDK.Interfaces/IPerfEvent"; import { IPerfManager, IPerfManagerProvider } from "../JavaScriptSDK.Interfaces/IPerfManager"; @@ -71,15 +71,17 @@ export class PerfEvent implements IPerfEvent { if (isFunction(payloadDetails)) { // Create an accessor to minimize the potential performance impact of executing the payloadDetails callback let theDetails:any; - objDefineAccessors(_self, "payload", () => { - // Delay the execution of the payloadDetails until needed - if (!theDetails && isFunction(payloadDetails)) { - theDetails = payloadDetails(); - // clear it out now so the referenced objects can be garbage collected - payloadDetails = null; - } + objDefine(_self, "payload", { + g: () => { + // Delay the execution of the payloadDetails until needed + if (!theDetails && isFunction(payloadDetails)) { + theDetails = payloadDetails(); + // clear it out now so the referenced objects can be garbage collected + payloadDetails = null; + } - return theDetails; + return theDetails; + } }); } diff --git a/shared/AppInsightsCore/src/applicationinsights-core-js.ts b/shared/AppInsightsCore/src/applicationinsights-core-js.ts index 5b021151d..32e9770f7 100644 --- a/shared/AppInsightsCore/src/applicationinsights-core-js.ts +++ b/shared/AppInsightsCore/src/applicationinsights-core-js.ts @@ -2,6 +2,7 @@ // Licensed under the MIT License. export { IConfiguration } from "./JavaScriptSDK.Interfaces/IConfiguration"; export { IChannelControls, MinChannelPriorty } from "./JavaScriptSDK.Interfaces/IChannelControls"; +export { IChannelControlsHost } from "./JavaScriptSDK.Interfaces/IChannelControlsHost"; export { ITelemetryPlugin, IPlugin } from "./JavaScriptSDK.Interfaces/ITelemetryPlugin"; export { IAppInsightsCore, ILoadedPlugin } from "./JavaScriptSDK.Interfaces/IAppInsightsCore"; export { ITelemetryItem, ICustomProperties, Tags } from "./JavaScriptSDK.Interfaces/ITelemetryItem";