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] 29445638: Fix Promise Initialization Sender Config Issue #2413

Merged
merged 3 commits into from
Sep 16, 2024
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
81 changes: 75 additions & 6 deletions AISKU/Tests/Unit/src/applicationinsights.e2e.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ export class ApplicationInsightsTests extends AITestClass {
});

this.testCaseAsync({
name: "Init: init with cs promise, change with cs string",
name: "Init: init with cs promise, when it is resolved and then change with cs string",
stepDelay: 100,
useFakeTimers: true,
steps: [() => {
Expand All @@ -273,6 +273,7 @@ export class ApplicationInsightsTests extends AITestClass {
let csPromise = createAsyncResolvedPromise("InstrumentationKey=testIkey;ingestionendpoint=testUrl");
this._config.connectionString = csPromise;
this._config.initTimeOut= 80000;
this._ctx.csPromise = csPromise;


let init = new ApplicationInsights({
Expand All @@ -284,13 +285,79 @@ export class ApplicationInsightsTests extends AITestClass {
let core = this._ai.core;
let 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();
let csPromise = this._ctx.csPromise;
let config = this._ai.config;

if (csPromise.state === "resolved" && 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");

config.connectionString = "InstrumentationKey=testIkey1;ingestionendpoint=testUrl1";
this.clock.tick(1);
let status = core.activeStatus && core.activeStatus();
// promise is not resolved, no new changes applied
Assert.equal(status, ActiveStatus.ACTIVE, "status should be set to active test1");
return true;
}
return false;
}, "Wait for promise response" + new Date().toISOString(), 60, 1000) as any).concat(PollingAssert.createPollingAssert(() => {
let core = this._ai.core
let activeStatus = core.activeStatus && core.activeStatus();

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

this.testCaseAsync({
name: "Init: init with cs promise and change with cs string at the same time",
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;
this._ctx.csPromise = csPromise;


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");
Assert.equal(status, ActiveStatus.ACTIVE, "active status should be set to active in next executing cycle");
// Assert.equal(status, ActiveStatus.PENDING, "status should be set to pending test1");



}].concat(PollingAssert.createPollingAssert(() => {
Expand All @@ -306,6 +373,7 @@ export class ApplicationInsightsTests extends AITestClass {
}, "Wait for promise response" + new Date().toISOString(), 60, 1000) as any)
});


this.testCaseAsync({
name: "Init: init with cs promise and offline channel",
stepDelay: 100,
Expand Down Expand Up @@ -345,7 +413,8 @@ export class ApplicationInsightsTests extends AITestClass {
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");
Assert.equal(status, ActiveStatus.ACTIVE, "active status should be set to active in next executing cycle");
// Assert.equal(status, ActiveStatus.PENDING, "status should be set to pending test1");


}].concat(PollingAssert.createPollingAssert(() => {
Expand Down Expand Up @@ -387,12 +456,12 @@ export class ApplicationInsightsTests extends AITestClass {
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");
Assert.equal(status, ActiveStatus.ACTIVE, "active status should be set to active in next executing cycle");
//Assert.equal(status, ActiveStatus.PENDING, "status should be set to pending");


}].concat(PollingAssert.createPollingAssert(() => {
Expand Down
58 changes: 57 additions & 1 deletion AISKU/Tests/Unit/src/sender.e2e.tests.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { ApplicationInsights, IApplicationInsights } from '../../../src/applicationinsights-web'
import { Sender } from '@microsoft/applicationinsights-channel-js';
import { BreezeChannelIdentifier, utlGetSessionStorage, utlRemoveSessionStorage } from '@microsoft/applicationinsights-common';
import { dumpObj, getJSON, isArray } from '@microsoft/applicationinsights-core-js';
import { ActiveStatus, dumpObj, getJSON, isArray } from '@microsoft/applicationinsights-core-js';
import { SinonSpy } from 'sinon';
import { Assert, AITestClass, PollingAssert} from "@microsoft/ai-test-framework"
import { createAsyncResolvedPromise } from '@nevware21/ts-async';

export class SenderE2ETests extends AITestClass {
private readonly _instrumentationKey = 'b7170927-2d1c-44f1-acec-59f4e1751c11';
Expand Down Expand Up @@ -127,6 +128,61 @@ export class SenderE2ETests extends AITestClass {
.concat(PollingAssert.createPollingAssert(() => this.successSpy.called && this.isSessionSentEmpty(), "SentBuffer Session storage is empty", 15, 1000) as any)
.concat(PollingAssert.createPollingAssert(() => this.successSpy.called && this.isSessionEmpty(), "Buffer Session storage is empty", 15, 1000) as any)
});

this.testCaseAsync({
name: 'SendBuffer: Session storage is cleared after a send with cs promise',
stepDelay: this.delay,
steps: [
() => {
if (this._ai && this._ai.unload) {
this._ai.unload(false);
}

let csPromise = createAsyncResolvedPromise(`InstrumentationKey=${this._instrumentationKey}`);
let init = new ApplicationInsights({
config: {
connectionString: csPromise,
loggingLevelConsole: 999,
extensionConfig: {
'AppInsightsChannelPlugin': {
maxBatchInterval: 2000,
maxBatchSizeInBytes: 10*1024*1024 // 10 MB
},
["AppInsightsCfgSyncPlugin"]: {
cfgUrl: ""
}

}
},
queue: [],
version: 2.0
});
this._ai = init.loadAppInsights();

// Setup Sinon stuff
this._sender = this._ai.getPlugin<Sender>(BreezeChannelIdentifier).plugin;
this._sender._buffer.clear();
this.errorSpy = this.sandbox.spy(this._sender, '_onError');
this.successSpy = this.sandbox.spy(this._sender, '_onSuccess');
this.loggingSpy = this.sandbox.stub(this._ai.appInsights.core.logger, 'throwInternal');
this.clearSpy = this.sandbox.spy(this._sender._buffer, 'clearSent');
this._ai.trackTrace({message: 'test trace'});
}
].concat(PollingAssert.createPollingAssert(() => {
let core = this._ai.appInsights.core
let activeStatus = core.activeStatus && core.activeStatus();

if (activeStatus === ActiveStatus.ACTIVE ) {
Assert.equal(this._instrumentationKey, core.config.instrumentationKey, "ikey should be set");
return true;
}
return false;
}, "Wait for promise response" + new Date().toISOString(), 60, 1000) as any)
.concat(this.waitForResponse())
.concat(this.boilerPlateAsserts)
.concat(PollingAssert.createPollingAssert(() => this.successSpy.called && this.isSessionSentEmpty(), "SentBuffer Session storage is empty", 15, 1000) as any)
.concat(PollingAssert.createPollingAssert(() => this.successSpy.called && this.isSessionEmpty(), "Buffer Session storage is empty", 15, 1000) as any)
});
}

private addTrackEndpointTests(): void {
Expand Down
10 changes: 5 additions & 5 deletions AISKU/src/AISku.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
IDependencyListenerHandler
} from "@microsoft/applicationinsights-dependencies-js";
import { PropertiesPlugin } from "@microsoft/applicationinsights-properties-js";
import { IPromise, createAsyncPromise, createPromise, doAwaitResponse } from "@nevware21/ts-async";
import { IPromise, createPromise, createSyncPromise, doAwaitResponse } from "@nevware21/ts-async";
import { arrForEach, arrIndexOf, isPromiseLike, objDefine, objForEachKey, strIndexOf, throwUnsupported } from "@nevware21/ts-utils";
import { IApplicationInsights } from "./IApplicationInsights";
import {
Expand Down Expand Up @@ -203,7 +203,7 @@ export class AppInsightsSku implements IApplicationInsights {
let configCs = _config.connectionString;

function _parseCs() {
return createAsyncPromise<ConnectionString>((resolve, reject) => {
return createSyncPromise<ConnectionString>((resolve, reject) => {
doAwaitResponse(configCs, (res) => {
let curCs = res && res.value;
let parsedCs = null;
Expand All @@ -220,7 +220,7 @@ export class AppInsightsSku implements IApplicationInsights {
}

if (isPromiseLike(configCs)) {
let ikeyPromise = createAsyncPromise<string>((resolve, reject) => {
let ikeyPromise = createSyncPromise<string>((resolve, reject) => {
_parseCs().then((cs) => {
let ikey = _config.instrumentationKey;
ikey = cs && cs.instrumentationkey || ikey;
Expand All @@ -235,7 +235,7 @@ export class AppInsightsSku implements IApplicationInsights {

let url: IPromise<string> | string = _config.userOverrideEndpointUrl;
if (isNullOrUndefined(url)) {
url = createAsyncPromise<string>((resolve, reject) => {
url = createSyncPromise<string>((resolve, reject) => {
_parseCs().then((cs) => {
let url = _config.endpointUrl;
let ingest = cs && cs.ingestionendpoint;
Expand All @@ -254,7 +254,7 @@ export class AppInsightsSku implements IApplicationInsights {
_config.endpointUrl = url;

}
if (isString(configCs)) {
if (isString(configCs) && configCs) {
// confirm if promiselike function present
// handle cs promise here
// add cases to oneNote
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ export class SenderTests extends AITestClass {
if (activeStatus === ActiveStatus.ACTIVE) {
QUnit.assert.equal("testIkey", core.config.instrumentationKey, "ikey should be set");
QUnit.assert.equal("testUrl", core.config.endpointUrl ,"endpoint shoule be set");
// getExtCfg only finds undefined values from core
let senderConfig = this._sender._senderConfig;
QUnit.assert.equal("testIkey", senderConfig.instrumentationKey, "sender ikey should be set");
QUnit.assert.equal("testUrl", senderConfig.endpointUrl ,"sender endpoint shoule be set");
Karlie-777 marked this conversation as resolved.
Show resolved Hide resolved

return true;
}
return false;
Expand Down
15 changes: 13 additions & 2 deletions channels/applicationinsights-channel-js/src/Sender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import {
prependTransports, runTargetUnload
} from "@microsoft/applicationinsights-core-js";
import { IPromise } from "@nevware21/ts-async";
import { ITimerHandler, isNumber, isString, isTruthy, objDeepFreeze, objDefine, scheduleTimeout } from "@nevware21/ts-utils";
import {
ITimerHandler, isNumber, isPromiseLike, isString, isTruthy, objDeepFreeze, objDefine, scheduleTimeout
} from "@nevware21/ts-utils";
import {
DependencyEnvelopeCreator, EventEnvelopeCreator, ExceptionEnvelopeCreator, MetricEnvelopeCreator, PageViewEnvelopeCreator,
PageViewPerformanceEnvelopeCreator, TraceEnvelopeCreator
Expand Down Expand Up @@ -264,8 +266,17 @@ export class Sender extends BaseTelemetryPlugin implements IChannelControls {
utlSetStoragePrefix(config.storagePrefix);
}
let ctx = createProcessTelemetryContext(null, config, core);
// getExtCfg only finds undefined values from core
let senderConfig = ctx.getExtCfg(identifier, defaultAppInsightsChannelConfig);
if(isPromiseLike(senderConfig.endpointUrl)) {
// if it is promise, means the endpoint url is from core.endpointurl
senderConfig.endpointUrl = config.endpointUrl as any;
}

if(isPromiseLike(senderConfig.instrumentationKey)) {
// if it is promise, means the endpoint url is from core.endpointurl
senderConfig.instrumentationKey = config.instrumentationKey as any;
}
objDefine(_self, "_senderConfig", {
g: function() {
return senderConfig;
Expand Down Expand Up @@ -356,7 +367,7 @@ export class Sender extends BaseTelemetryPlugin implements IChannelControls {
_self._sample = new Sample(senderConfig.samplingPercentage, diagLog);

_instrumentationKey = senderConfig.instrumentationKey;
if(!_validateInstrumentationKey(_instrumentationKey, config)) {
if(!isPromiseLike(_instrumentationKey) && !_validateInstrumentationKey(_instrumentationKey, config)) {
_throwInternal(diagLog,
eLoggingSeverity.CRITICAL,
_eInternalMessageId.InvalidInstrumentationKey, "Invalid Instrumentation key " + _instrumentationKey);
Expand Down
Loading
Loading