Skip to content
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

[Main][Task]27939476: Initialization with iKey and endpoint to be promises #2340

Merged
merged 22 commits into from
Jun 26, 2024
Merged
1 change: 1 addition & 0 deletions .aiAutoMinify.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"eBatchDiscardedReason",
"FeatureOptInMode",
"CdnFeatureMode",
"eActiveStatus",
"eLoggingSeverity",
"_eInternalMessageId",
"SendRequestReason",
Expand Down
4 changes: 2 additions & 2 deletions AISKU/Tests/Unit/src/AISKUSize.Tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { Snippet } from "../../../src/Snippet";
import { utlRemoveSessionStorage } from "@microsoft/applicationinsights-common";

export class AISKUSizeCheck extends AITestClass {
private readonly MAX_RAW_SIZE = 141;
private readonly MAX_BUNDLE_SIZE = 141;
private readonly MAX_RAW_SIZE = 142;
private readonly MAX_BUNDLE_SIZE = 142;
private readonly MAX_RAW_DEFLATE_SIZE = 57;
private readonly MAX_BUNDLE_DEFLATE_SIZE = 57;
private readonly rawFilePath = "../dist/es5/applicationinsights-web.min.js";
Expand Down
215 changes: 214 additions & 1 deletion AISKU/Tests/Unit/src/applicationinsights.e2e.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { SinonSpy } from 'sinon';
import { ApplicationInsights } from '../../../src/applicationinsights-web'
import { Sender } from '@microsoft/applicationinsights-channel-js';
import { IDependencyTelemetry, ContextTagKeys, Event, Trace, Exception, Metric, PageView, PageViewPerformance, RemoteDependencyData, DistributedTracingModes, RequestHeaders, IAutoExceptionTelemetry, BreezeChannelIdentifier, IConfig } from '@microsoft/applicationinsights-common';
import { ITelemetryItem, getGlobal, newId, dumpObj, BaseTelemetryPlugin, IProcessTelemetryContext, __getRegisteredEvents, arrForEach, IConfiguration, FeatureOptInMode } from "@microsoft/applicationinsights-core-js";
import { ITelemetryItem, getGlobal, newId, dumpObj, BaseTelemetryPlugin, IProcessTelemetryContext, __getRegisteredEvents, arrForEach, IConfiguration, ActiveStatus, FeatureOptInMode } from "@microsoft/applicationinsights-core-js";
import { TelemetryContext } from '@microsoft/applicationinsights-properties-js';
import { createAsyncResolvedPromise } from '@nevware21/ts-async';
import { CONFIG_ENDPOINT_URL } from '../../../src/InternalConstants';
import { OfflineChannel } from '@microsoft/applicationinsights-offlinechannel-js';

Expand Down Expand Up @@ -47,6 +48,7 @@ export class ApplicationInsightsTests extends AITestClass {
private _appId: string;
private _ctx: any;


constructor(testName?: string) {
super(testName || "ApplicationInsightsTests");
}
Expand Down Expand Up @@ -250,6 +252,217 @@ export class ApplicationInsightsTests extends AITestClass {
}
});

this.testCaseAsync({
name: "Init: init with cs promise, change with cs string",
stepDelay: 100,
useFakeTimers: true,
steps: [() => {

// unload previous one first
let oriInst = this._ai;
if (oriInst && oriInst.unload) {
// force unload
oriInst.unload(false);
}

if (oriInst && oriInst["dependencies"]) {
oriInst["dependencies"].teardown();
}

this._config = this._getTestConfig(this._sessionPrefix);
let csPromise = createAsyncResolvedPromise("InstrumentationKey=testIkey;ingestionendpoint=testUrl");
this._config.connectionString = csPromise;
this._config.initTimeOut= 80000;


let init = new ApplicationInsights({
config: this._config
});
init.loadAppInsights();
this._ai = init;
let config = this._ai.config;
let core = this._ai.core;
let status = core.activeStatus && core.activeStatus();
Assert.equal(status, ActiveStatus.PENDING, "status should be set to pending");


config.connectionString = "InstrumentationKey=testIkey1;ingestionendpoint=testUrl1";
this.clock.tick(1);
status = core.activeStatus && core.activeStatus();
// promise is not resolved, no new changes applied
Assert.equal(status, ActiveStatus.PENDING, "status should be set to pending test1");


}].concat(PollingAssert.createPollingAssert(() => {
let core = this._ai.core
let activeStatus = core.activeStatus && core.activeStatus();

if (activeStatus === ActiveStatus.ACTIVE) {
Assert.equal("testIkey", core.config.instrumentationKey, "ikey should be set");
Assert.equal("testUrl/v2/track", core.config.endpointUrl ,"endpoint shoule be set");
return true;
}
return false;
}, "Wait for promise response" + new Date().toISOString(), 60, 1000) as any)
});

this.testCaseAsync({
name: "Init: init with cs promise and offline channel",
stepDelay: 100,
useFakeTimers: true,
steps: [() => {

// unload previous one first
let oriInst = this._ai;
if (oriInst && oriInst.unload) {
// force unload
oriInst.unload(false);
}

if (oriInst && oriInst["dependencies"]) {
oriInst["dependencies"].teardown();
}

this._config = this._getTestConfig(this._sessionPrefix);
let csPromise = createAsyncResolvedPromise("InstrumentationKey=testIkey;ingestionendpoint=testUrl");
this._config.connectionString = csPromise;
let offlineChannel = new OfflineChannel();
this._config.channels = [[offlineChannel]];
this._config.initTimeOut= 80000;


let init = new ApplicationInsights({
config: this._config
});
init.loadAppInsights();
this._ai = init;
let config = this._ai.config;
let core = this._ai.core;
let status = core.activeStatus && core.activeStatus();
Assert.equal(status, ActiveStatus.PENDING, "status should be set to pending");


config.connectionString = "InstrumentationKey=testIkey1;ingestionendpoint=testUrl1"
this.clock.tick(1);
status = core.activeStatus && core.activeStatus();
Assert.equal(status, ActiveStatus.PENDING, "status should be set to pending test1");


}].concat(PollingAssert.createPollingAssert(() => {
let core = this._ai.core
let activeStatus = core.activeStatus && core.activeStatus();

if (activeStatus === ActiveStatus.ACTIVE) {
Assert.equal("testIkey", core.config.instrumentationKey, "ikey should be set");
Assert.equal("testUrl/v2/track", core.config.endpointUrl ,"endpoint shoule be set");
let sendChannel = this._ai.getPlugin(BreezeChannelIdentifier);
let offlineChannelPlugin = this._ai.getPlugin("OfflineChannel").plugin;
Assert.equal(sendChannel.plugin.isInitialized(), true, "sender is initialized");
Assert.equal(offlineChannelPlugin.isInitialized(), true, "offline channel is initialized");
let urlConfig = offlineChannelPlugin["_getDbgPlgTargets"]()[0];
Assert.ok(urlConfig, "offline url config is initialized");
return true;
}
return false;
}, "Wait for promise response" + new Date().toISOString(), 60, 1000) as any)
});



this.testCaseAsync({
name: "Init: init with cs string, change with cs promise",
stepDelay: 100,
useFakeTimers: true,
steps: [() => {
let config = this._ai.config;
let expectedIkey = ApplicationInsightsTests._instrumentationKey;
let expectedConnectionString = ApplicationInsightsTests._connectionString;
let expectedEndpointUrl = "https://dc.services.visualstudio.com/v2/track";
Assert.ok(config, "ApplicationInsights config exists");
Assert.equal(expectedConnectionString, config.connectionString, "connection string is set");
Assert.equal(expectedIkey, config.instrumentationKey, "ikey is set");
Assert.equal(expectedEndpointUrl, config.endpointUrl, "endpoint url is set from connection string");
let core = this._ai.core;
let status = core.activeStatus && core.activeStatus();
Assert.equal(status, ActiveStatus.ACTIVE, "status should be set to active");

let csPromise = createAsyncResolvedPromise("InstrumentationKey=testIkey;ingestionendpoint=testUrl");

config.connectionString = csPromise;
config.initTimeOut = 80000;
this.clock.tick(1);
status = core.activeStatus && core.activeStatus();
Assert.equal(status, ActiveStatus.PENDING, "status should be set to pending");


}].concat(PollingAssert.createPollingAssert(() => {
let core = this._ai.core
let activeStatus = core.activeStatus && core.activeStatus();

if (activeStatus === ActiveStatus.ACTIVE) {
Assert.equal("testIkey", core.config.instrumentationKey, "ikey should be set");
Assert.equal("testUrl/v2/track", core.config.endpointUrl ,"endpoint shoule be set");
return true;
}
return false;
}, "Wait for promise response" + new Date().toISOString(), 60, 1000) as any)
});

this.testCaseAsync({
name: "Init: init with cs null, ikey promise, endpoint promise",
stepDelay: 100,
useFakeTimers: true,
steps: [() => {

// unload previous one first
let oriInst = this._ai;
if (oriInst && oriInst.unload) {
// force unload
oriInst.unload(false);
}

if (oriInst && oriInst["dependencies"]) {
oriInst["dependencies"].teardown();
}

this._config = this._getTestConfig(this._sessionPrefix);
let ikeyPromise = createAsyncResolvedPromise("testIkey");
let endpointPromise = createAsyncResolvedPromise("testUrl");
//let csPromise = createAsyncResolvedPromise("InstrumentationKey=testIkey;ingestionendpoint=testUrl");
//this._config.connectionString = csPromise;
this._config.connectionString = null;
this._config.instrumentationKey = ikeyPromise;
this._config.endpointUrl = endpointPromise;
this._config.initTimeOut= 80000;



let init = new ApplicationInsights({
config: this._config
});
init.loadAppInsights();
this._ai = init;
let config = this._ai.config;
let core = this._ai.core;
let status = core.activeStatus && core.activeStatus();
Assert.equal(status, ActiveStatus.PENDING, "status should be set to pending");
Assert.equal(config.connectionString,null, "connection string shoule be null");


}].concat(PollingAssert.createPollingAssert(() => {
let core = this._ai.core
let activeStatus = core.activeStatus && core.activeStatus();

if (activeStatus === ActiveStatus.ACTIVE) {
Assert.equal("testIkey", core.config.instrumentationKey, "ikey should be set");
Assert.equal("testUrl", core.config.endpointUrl ,"endpoint shoule be set");
return true;
}
return false;
}, "Wait for promise response" + new Date().toISOString(), 60, 1000) as any)
});


this.testCase({
name: "CfgSync DynamicConfigTests: Prod CDN is Fetched and feature is turned on/off as expected",
useFakeTimers: true,
Expand Down
47 changes: 43 additions & 4 deletions AISKU/src/AISku.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import {
IDependencyListenerHandler
} from "@microsoft/applicationinsights-dependencies-js";
import { PropertiesPlugin } from "@microsoft/applicationinsights-properties-js";
import { IPromise, createPromise } from "@nevware21/ts-async";
import { arrForEach, arrIndexOf, objDefine, objForEachKey, strIndexOf, throwUnsupported } from "@nevware21/ts-utils";
import { IPromise, createAsyncPromise, createPromise, doAwaitResponse } from "@nevware21/ts-async";
import { arrForEach, arrIndexOf, isPromiseLike, objDefine, objForEachKey, strIndexOf, throwUnsupported } from "@nevware21/ts-utils";
import { IApplicationInsights } from "./IApplicationInsights";
import {
CONFIG_ENDPOINT_URL, STR_ADD_TELEMETRY_INITIALIZER, STR_CLEAR_AUTHENTICATED_USER_CONTEXT, STR_EVT_NAMESPACE, STR_GET_COOKIE_MGR,
Expand Down Expand Up @@ -200,8 +200,47 @@ export class AppInsightsSku implements IApplicationInsights {

// Will get recalled if any referenced values are changed
_addUnloadHook(onConfigChange(cfgHandler, () => {
if (_config.connectionString) {
const cs = parseConnectionString(_config.connectionString);
let configCs = _config.connectionString;

if (isPromiseLike(configCs)) {
let ikeyPromise = createAsyncPromise<string>((resolve, reject) => {
doAwaitResponse(configCs, (res) => {
let curCs = res.value;
let ikey = _config.instrumentationKey;
if (!res.rejected && curCs) {
// replace cs with resolved values in case of circular promises
_config.connectionString = curCs;
Karlie-777 marked this conversation as resolved.
Show resolved Hide resolved
let resolvedCs = parseConnectionString(curCs);
ikey = resolvedCs.instrumentationkey || ikey;
}
resolve(ikey);
});

});

let urlPromise = createAsyncPromise<string>((resolve, reject) => {
Karlie-777 marked this conversation as resolved.
Show resolved Hide resolved
doAwaitResponse(configCs, (res) => {
let curCs = res.value;
let url = _config.endpointUrl;
if (!res.rejected && curCs) {
let resolvedCs = parseConnectionString(curCs);
let ingest = resolvedCs.ingestionendpoint;
url = ingest? ingest + DEFAULT_BREEZE_PATH : url;
}
resolve(url);
Karlie-777 marked this conversation as resolved.
Show resolved Hide resolved
});

});

_config.instrumentationKey = ikeyPromise;
_config.endpointUrl = _config.userOverrideEndpointUrl || urlPromise;

}
if (isString(configCs)) {
// confirm if promiselike function present
// handle cs promise here
// add cases to oneNote
const cs = parseConnectionString(configCs);
const ingest = cs.ingestionendpoint;
_config.endpointUrl = _config.userOverrideEndpointUrl ? _config.userOverrideEndpointUrl : ingest + DEFAULT_BREEZE_PATH; // add /v2/track
_config.instrumentationKey = cs.instrumentationkey || _config.instrumentationKey;
Expand Down
8 changes: 4 additions & 4 deletions AISKULight/Tests/Unit/src/AISKULightSize.Tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { AITestClass, Assert } from "@microsoft/ai-test-framework";
import * as pako from "pako";

export class AISKULightSizeCheck extends AITestClass {
private readonly MAX_RAW_SIZE = 88;
private readonly MAX_BUNDLE_SIZE = 88;
private readonly MAX_RAW_DEFLATE_SIZE = 36;
private readonly MAX_BUNDLE_DEFLATE_SIZE = 36;
private readonly MAX_RAW_SIZE = 89;
private readonly MAX_BUNDLE_SIZE = 89;
private readonly MAX_RAW_DEFLATE_SIZE = 37;
private readonly MAX_BUNDLE_DEFLATE_SIZE = 37;
private readonly rawFilePath = "../dist/es5/applicationinsights-web-basic.min.js";
private readonly currentVer = "3.2.2";
private readonly prodFilePath = `../browser/es5/aib.${this.currentVer[0]}.min.js`;
Expand Down
Loading
Loading