Skip to content

Commit

Permalink
[Main][Task]16238553: Provide an override option for the Sender (#2113)
Browse files Browse the repository at this point in the history
* update

* update

* update

* update

* update

* update

* update

* update

* update

* update

* update

* update

* update

* update

* update
  • Loading branch information
Karlie-777 authored Sep 27, 2023
1 parent ea6c550 commit f4da17e
Show file tree
Hide file tree
Showing 18 changed files with 713 additions and 400 deletions.
1 change: 1 addition & 0 deletions .aiAutoMinify.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"eLoggingSeverity",
"_eInternalMessageId",
"SendRequestReason",
"TransportType",
"TelemetryUnloadReason",
"TelemetryUpdateReason"
]
Expand Down
8 changes: 4 additions & 4 deletions AISKU/Tests/Unit/src/AISKUSize.Tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { Snippet } from "../../../src/Snippet";
import { utlRemoveSessionStorage } from "@microsoft/applicationinsights-common";

export class AISKUSizeCheck extends AITestClass {
private readonly MAX_RAW_SIZE = 133;
private readonly MAX_BUNDLE_SIZE = 133;
private readonly MAX_RAW_DEFLATE_SIZE = 53;
private readonly MAX_BUNDLE_DEFLATE_SIZE = 53;
private readonly MAX_RAW_SIZE = 134;
private readonly MAX_BUNDLE_SIZE = 134;
private readonly MAX_RAW_DEFLATE_SIZE = 54;
private readonly MAX_BUNDLE_DEFLATE_SIZE = 54;
private readonly rawFilePath = "../dist/es5/applicationinsights-web.min.js";
// Automatically updated by version scripts
private readonly currentVer = "3.0.3";
Expand Down
4 changes: 2 additions & 2 deletions AISKULight/Tests/Unit/src/AISKULightSize.Tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { AITestClass, Assert } from "@microsoft/ai-test-framework";
import * as pako from "pako";

export class AISKULightSizeCheck extends AITestClass {
private readonly MAX_RAW_SIZE = 80;
private readonly MAX_BUNDLE_SIZE = 80;
private readonly MAX_RAW_SIZE = 81;
private readonly MAX_BUNDLE_SIZE = 81;
private readonly MAX_RAW_DEFLATE_SIZE = 33;
private readonly MAX_BUNDLE_DEFLATE_SIZE = 33;
private readonly rawFilePath = "../dist/es5/applicationinsights-web-basic.min.js";
Expand Down
33 changes: 2 additions & 31 deletions channels/1ds-post-js/src/DataModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,10 @@
* File containing the interfaces for Post channel module.
*/
import {
IDiagnosticLogger, IExtendedTelemetryItem, IProcessTelemetryContext, ITelemetryPlugin, IUnloadHook, IValueSanitizer
IDiagnosticLogger, IExtendedTelemetryItem, IPayloadData, IProcessTelemetryContext, ITelemetryPlugin, IUnloadHook, IValueSanitizer,
IXHROverride
} from "@microsoft/1ds-core-js";

/** IPayloadData describes interface of payload sent via POST channel */
export interface IPayloadData {
urlString: string;
data: Uint8Array | string;
headers?: { [name: string]: string };
timeout?: number;
disableXhrSync?: boolean;
disableFetchKeepAlive?: boolean;
}

/**
* Defines the function signature for the Payload Preprocessor.
* @param payload - The Initial constructed payload that if not modified should be passed onto the callback function.
Expand Down Expand Up @@ -245,26 +236,6 @@ export interface IChannelConfiguration {
addNoResponse?: boolean;
}

/**
* SendPOSTFunction type defines how an HTTP POST request is sent to an ingestion server
* @param payload - The payload object that should be sent, contains the url, bytes/string and headers for the request
* @param oncomplete - The function to call once the request has completed whether a success, failure or timeout
* @param sync - A boolean flag indicating whether the request should be sent as a synchronous request.
*/
export type SendPOSTFunction = (payload: IPayloadData, oncomplete: (status: number, headers: { [headerName: string]: string; }, response?: string) => void, sync?: boolean) => void;

/**
* The IXHROverride interface overrides the way HTTP requests are sent.
*/
export interface IXHROverride {
/**
* This method sends data to the specified URI using a POST request. If sync is true,
* then the request is sent synchronously. The <i>oncomplete</i> function should always be called after the request is
* completed (either successfully or timed out or failed due to errors).
*/
sendPOST: SendPOSTFunction;
}

/**
* An interface which extends the telemetry event to track send attempts.
*/
Expand Down
16 changes: 8 additions & 8 deletions channels/1ds-post-js/src/HttpManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@
*/
import dynamicProto from "@microsoft/dynamicproto-js";
import {
EventSendType, FullVersionString, IAppInsightsCore, ICookieMgr, IDiagnosticLogger, IExtendedConfiguration, IPerfEvent, IUnloadHook,
SendRequestReason, TransportType, _eExtendedInternalMessageId, _eInternalMessageId, _throwInternal, _warnToConsole, arrForEach, dateNow,
doPerf, dumpObj, eLoggingSeverity, extend, getLocation, getNavigator, getTime, hasOwnProperty, isArray, isBeaconsSupported,
isFetchSupported, isNullOrUndefined, isNumber, isReactNative, isString, isUndefined, isValueAssigned, isXhrSupported, objForEachKey,
objKeys, onConfigChange, openXhr, strTrim, strUndefined, useXDomainRequest
EventSendType, FullVersionString, IAppInsightsCore, ICookieMgr, IDiagnosticLogger, IExtendedConfiguration, IPayloadData, IPerfEvent,
IUnloadHook, IXHROverride, OnCompleteCallback, SendPOSTFunction, SendRequestReason, TransportType, _eExtendedInternalMessageId,
_eInternalMessageId, _throwInternal, _warnToConsole, arrForEach, dateNow, doPerf, dumpObj, eLoggingSeverity, extend, getLocation,
getNavigator, getTime, hasOwnProperty, isArray, isBeaconsSupported, isFetchSupported, isNullOrUndefined, isNumber, isReactNative,
isString, isUndefined, isValueAssigned, isXhrSupported, objForEachKey, objKeys, onConfigChange, openXhr, strTrim, strUndefined,
useXDomainRequest
} from "@microsoft/1ds-core-js";
import { arrAppend } from "@nevware21/ts-utils";
import { BatchNotificationAction, BatchNotificationActions } from "./BatchNotificationActions";
import { ClockSkewManager } from "./ClockSkewManager";
import {
EventBatchNotificationReason, IChannelConfiguration, ICollectorResult, IPayloadData, IPostChannel, IPostTransmissionTelemetryItem,
IXHROverride, PayloadListenerFunction, PayloadPreprocessorFunction, SendPOSTFunction
EventBatchNotificationReason, IChannelConfiguration, ICollectorResult, IPostChannel, IPostTransmissionTelemetryItem,
PayloadListenerFunction, PayloadPreprocessorFunction
} from "./DataModels";
import { EventBatch } from "./EventBatch";
import {
Expand Down Expand Up @@ -77,7 +78,6 @@ _addCollectorHeaderQsMapping(STR_TIME_DELTA_TO_APPLY, STR_TIME_DELTA_TO_APPLY);
_addCollectorHeaderQsMapping(STR_UPLOAD_TIME, STR_UPLOAD_TIME);
_addCollectorHeaderQsMapping(STR_AUTH_XTOKEN, STR_AUTH_XTOKEN);

type OnCompleteCallback = (status: number, headers: { [headerName: string]: string }, response?: string) => void;

function _getResponseText(xhr: XMLHttpRequest | IXDomainRequest) {
try {
Expand Down
6 changes: 3 additions & 3 deletions channels/1ds-post-js/src/Index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
* File to export public classes.
*/

import { IPayloadData, IXHROverride, OnCompleteCallback, SendPOSTFunction } from "@microsoft/1ds-core-js";
import {
BE_PROFILE, IChannelConfiguration, IPayloadData, IPostChannel, IXHROverride, NRT_PROFILE, PayloadListenerFunction,
PayloadPreprocessorFunction, RT_PROFILE, SendPOSTFunction
BE_PROFILE, IChannelConfiguration, IPostChannel, NRT_PROFILE, PayloadListenerFunction, PayloadPreprocessorFunction, RT_PROFILE
} from "./DataModels";
import { PostChannel } from "./PostChannel";

export {
PostChannel, IChannelConfiguration,
BE_PROFILE, NRT_PROFILE, RT_PROFILE, IXHROverride, IPostChannel,
SendPOSTFunction, IPayloadData, PayloadPreprocessorFunction, PayloadListenerFunction
SendPOSTFunction, IPayloadData, PayloadPreprocessorFunction, PayloadListenerFunction, OnCompleteCallback
};
4 changes: 2 additions & 2 deletions channels/1ds-post-js/test/Unit/src/HttpManagerTest.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { AITestClass } from "@microsoft/ai-test-framework";
import { HttpManager } from "../../../src/HttpManager";
import { AppInsightsCore, EventSendType, IExtendedConfiguration, SendRequestReason, TransportType, isBeaconsSupported } from "@microsoft/1ds-core-js";
import { PostChannel, IXHROverride } from "../../../src/Index";
import { IPostTransmissionTelemetryItem, IPayloadData, EventBatchNotificationReason, IChannelConfiguration } from "../../../src/DataModels";
import { PostChannel, IXHROverride, IPayloadData } from "../../../src/Index";
import { IPostTransmissionTelemetryItem, EventBatchNotificationReason, IChannelConfiguration } from "../../../src/DataModels";
import { EventBatch } from "../../../src/EventBatch";

interface EventDetail {
Expand Down
4 changes: 2 additions & 2 deletions channels/1ds-post-js/test/Unit/src/PostChannelTest.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AITestClass } from "@microsoft/ai-test-framework";
import { IExtendedConfiguration, NotificationManager, AppInsightsCore, EventLatency, ITelemetryItem, IExtendedTelemetryItem, SendRequestReason, EventSendType, isFetchSupported, objKeys, arrForEach, isBeaconsSupported } from '@microsoft/1ds-core-js';
import { PostChannel, IXHROverride, } from '../../../src/Index';
import { IPostTransmissionTelemetryItem, IPayloadData, IChannelConfiguration } from '../../../src/DataModels';
import { PostChannel, IXHROverride, IPayloadData } from '../../../src/Index';
import { IPostTransmissionTelemetryItem, IChannelConfiguration } from '../../../src/DataModels';
import { SinonSpy } from 'sinon';

interface IEventsSendRequests {
Expand Down
4 changes: 2 additions & 2 deletions channels/1ds-post-js/test/Unit/src/SerializerTest.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { AITestClass } from "@microsoft/ai-test-framework";
import { HttpManager } from '../../../src/HttpManager';
import { AppInsightsCore, EventLatency, IEventProperty } from '@microsoft/1ds-core-js';
import { PostChannel, IXHROverride } from '../../../src/Index';
import { IPostTransmissionTelemetryItem, IPayloadData, EventBatchNotificationReason } from '../../../src/DataModels';
import { PostChannel, IXHROverride, IPayloadData } from '../../../src/Index';
import { IPostTransmissionTelemetryItem, EventBatchNotificationReason } from '../../../src/DataModels';
import { Serializer } from '../../../src/Serializer';


Expand Down
139 changes: 137 additions & 2 deletions channels/applicationinsights-channel-js/Tests/Unit/src/Sender.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Sender } from "../../../src/Sender";
import { createOfflineListener, IOfflineListener } from '../../../src/Offline';
import { EnvelopeCreator } from '../../../src/EnvelopeCreator';
import { Exception, CtxTagKeys, isBeaconApiSupported, DEFAULT_BREEZE_ENDPOINT, DEFAULT_BREEZE_PATH, utlCanUseSessionStorage, utlGetSessionStorage, utlSetSessionStorage } from "@microsoft/applicationinsights-common";
import { ITelemetryItem, AppInsightsCore, ITelemetryPlugin, DiagnosticLogger, NotificationManager, SendRequestReason, _eInternalMessageId, getGlobalInst, safeGetLogger, getJSON, isString, isArray, arrForEach, isBeaconsSupported } from "@microsoft/applicationinsights-core-js";
import { ITelemetryItem, AppInsightsCore, ITelemetryPlugin, DiagnosticLogger, NotificationManager, SendRequestReason, _eInternalMessageId, getGlobalInst, safeGetLogger, getJSON, isString, isArray, arrForEach, isBeaconsSupported, IXHROverride, IPayloadData, isFetchSupported} from "@microsoft/applicationinsights-core-js";
import { ArraySendBuffer, SessionStorageSendBuffer } from "../../../src/SendBuffer";
import { ISenderConfig } from "../../../src/Interfaces";

Expand Down Expand Up @@ -118,6 +118,8 @@ export class SenderTests extends AITestClass {
QUnit.assert.equal(undefined, defaultSenderConfig.customHeaders, "Channel default customHeaders config is set");
QUnit.assert.equal(undefined, defaultSenderConfig.convertUndefined, "Channel default convertUndefined config is set");
QUnit.assert.equal(10000, defaultSenderConfig.eventsLimitInMem, "Channel default eventsLimitInMem config is set");
QUnit.assert.equal(undefined, defaultSenderConfig.httpXHROverride, "Channel default httpXHROverride config is set");
QUnit.assert.equal(false, defaultSenderConfig.alwaysUseXhrOverride, "Channel default alwaysUseXhrOverride config is set");

//check dynamic config
core.config.extensionConfig = core.config.extensionConfig? core.config.extensionConfig : {};
Expand All @@ -131,7 +133,8 @@ export class SenderTests extends AITestClass {
isRetryDisabled: true,
disableXhr: true,
samplingPercentage: 90,
customHeaders: [{header: "header1",value:"value1"}]
customHeaders: [{header: "header1",value:"value1"}],
alwaysUseXhrOverride: true
}
core.config.extensionConfig[id] = config;
this.clock.tick(1);
Expand All @@ -145,6 +148,7 @@ export class SenderTests extends AITestClass {
QUnit.assert.equal(true, curSenderConfig.isRetryDisabled, "Channel isRetryDisabled config is dynamically set");
QUnit.assert.equal(90, curSenderConfig.samplingPercentage, "Channel samplingPercentage config is dynamically set");
QUnit.assert.deepEqual([{header: "header1",value:"value1"}], curSenderConfig.customHeaders, "Channel customHeaders config is dynamically set");
QUnit.assert.deepEqual(true, curSenderConfig.alwaysUseXhrOverride, "Channel alwaysUseXhrOverride config is dynamically set");

core.config.extensionConfig[this._sender.identifier].emitLineDelimitedJson = undefined;
core.config.extensionConfig[this._sender.identifier].endpointUrl = undefined;
Expand All @@ -154,6 +158,137 @@ export class SenderTests extends AITestClass {
}
});

this.testCase({
name: "Channel Config: Sender override can be handled correctly",
useFakeTimers: true,
test: () => {
let core = new AppInsightsCore();
let sentPayloadData: any[] = [];
var xhrOverride: IXHROverride = {
sendPOST: (payload: IPayloadData, oncomplete: (status: number, headers: {[headerName: string]: string;}, response?: string) => void, sync?: boolean) => {
sentPayloadData.push({payload: payload, sync: sync});
}
};

let coreConfig = {
instrumentationKey: "abc",
extensionConfig: {
[this._sender.identifier]: {
httpXHROverride: xhrOverride
}
}
}
let testBatch: string[] = ["test", "test1"];
const telemetryItem: ITelemetryItem = {
name: "fake item",
iKey: "test",
baseType: "some type",
baseData: {}
};
core.initialize(coreConfig, [this._sender]);

// with always override to false
QUnit.assert.deepEqual(xhrOverride, this._sender._senderConfig.httpXHROverride, "Channel httpXHROverride config is set");
QUnit.assert.deepEqual(false, this._sender._senderConfig.alwaysUseXhrOverride, "Channel alwaysUseXhrOverride config is set");
this._sender._sender(testBatch, true);
QUnit.assert.equal(0, sentPayloadData.length, "httpXHROverride is not called once with always override to false");
this._sender._sender(testBatch, false);
QUnit.assert.equal(0, sentPayloadData.length, "httpXHROverride is not called once with always override to false test1");

try {
this._sender.processTelemetry(telemetryItem);
} catch(e) {
QUnit.assert.ok(false, "Exception - " + e);
}
this._sender.onunloadFlush();
QUnit.assert.deepEqual(0, sentPayloadData.length, "httpXHROverride should not be called again test2");


// with always override to true
core.config.extensionConfig = core.config.extensionConfig || {};
core.config.extensionConfig[this._sender.identifier].alwaysUseXhrOverride = true;
this.clock.tick(1);
QUnit.assert.deepEqual(true, this._sender._senderConfig.alwaysUseXhrOverride, "Channel alwaysUseXhrOverride config is set to true dynamically");
this._sender._sender(testBatch, true);
QUnit.assert.deepEqual(1, sentPayloadData.length, "httpXHROverride should be called with always override to true");
let payload = sentPayloadData[0].payload;
let sync = sentPayloadData[0].sync;
QUnit.assert.equal(false, sync, "Channel httpXHROverride sync is called with false during send test1 (sender interface should be opposite with the sender)");
QUnit.assert.deepEqual(testBatch, payload.oriPayload, "Channel httpXHROverride sync is called with expected original payload");
QUnit.assert.deepEqual(this._sender._buffer.batchPayloads(testBatch),payload.data, "Channel httpXHROverride sync is called with expected batch payload");

try {
this._sender.processTelemetry(telemetryItem);
} catch(e) {
QUnit.assert.ok(false, "Exception - " + e);
}
this._sender.onunloadFlush();
QUnit.assert.deepEqual(2, sentPayloadData.length, "httpXHROverride should be called");
let data = sentPayloadData[1].payload.oriPayload;
payload = JSON.parse(data[0]);
QUnit.assert.deepEqual("test", payload.iKey, "httpXHROverride should send expected payload test1");
sync = sentPayloadData[1].sync;
QUnit.assert.equal(true, sync, "Channel httpXHROverride sync is called with true during send test2 (sender interface should be opposite with the sender)");

}
});

this.testCase({
name: "Channel Config: Invalid paylod Sender should not be sent",
useFakeTimers: true,
test: () => {
let core = new AppInsightsCore();
let sentPayloadData: any[] = [];
var xhrOverride: IXHROverride = {
sendPOST: (payload: IPayloadData, oncomplete: (status: number, headers: {[headerName: string]: string;}, response?: string) => void, sync?: boolean) => {
sentPayloadData.push({payload: payload, sync: sync});
}
};

let coreConfig = {
instrumentationKey: "abc",
extensionConfig: {
[this._sender.identifier]: {
httpXHROverride: xhrOverride,
alwaysUseXhrOverride: true
}
}
}
let testBatch: string[] = ["test", "test1"];

core.initialize(coreConfig, [this._sender]);

QUnit.assert.deepEqual(xhrOverride, this._sender._senderConfig.httpXHROverride, "Channel httpXHROverride config is set");
QUnit.assert.deepEqual(true, this._sender._senderConfig.alwaysUseXhrOverride, "Channel alwaysUseXhrOverride config is set");
// case 1: payload is null
this._sender._sender(null as any, true);
QUnit.assert.equal(0, sentPayloadData.length, "httpXHROverride is not called test1");
this._sender._sender(null as any, false);
QUnit.assert.equal(0, sentPayloadData.length, "httpXHROverride is not called once sync test1");

// case 2: payload is none array
this._sender._sender({} as any, true);
QUnit.assert.equal(0, sentPayloadData.length, "httpXHROverride is not called test2");
this._sender._sender({} as any, false);
QUnit.assert.equal(0, sentPayloadData.length, "httpXHROverride is not called once sync test2");

// case 3: payload is an empty array
this._sender._sender([] as any, true);
QUnit.assert.equal(0, sentPayloadData.length, "httpXHROverride is not called test3");
this._sender._sender([] as any, false);
QUnit.assert.equal(0, sentPayloadData.length, "httpXHROverride is not called once sync test3");


this._sender._sender(testBatch, true);
QUnit.assert.equal(1, sentPayloadData.length, "httpXHROverride is called test4");
this._sender._sender(testBatch, false);
QUnit.assert.equal(2, sentPayloadData.length, "httpXHROverride is called once sync test4");


}
});


this.testCase({
name: "Channel Config: sessionStorage change from true to false can be handled correctly",
useFakeTimers: true,
Expand Down
Loading

0 comments on commit f4da17e

Please sign in to comment.