Skip to content

Commit

Permalink
[Main][Task]26451789: Add Offline Support (#2241)
Browse files Browse the repository at this point in the history
* offline draft v1

* copy2

* add offline support

* update

* update

* update

* update

* update

* update

* update

* update

* update

* update

* update

* update

* update

* update

* update
  • Loading branch information
Karlie-777 committed Feb 3, 2024
1 parent 9e26c21 commit f9d5ec1
Show file tree
Hide file tree
Showing 59 changed files with 10,560 additions and 436 deletions.
11 changes: 11 additions & 0 deletions .aiAutoMinify.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"eStorageType",
"FieldType",
"eDistributedTracingModes",
"EventPersistenceValue",
"eOfflineValue",
"eRequestHeaders",
"DataPointType",
Expand Down Expand Up @@ -90,6 +91,16 @@
"constEnums": [
"EventBatchNotificationReason"
]
},
"@microsoft/applicationinsights-offlinechannel-js": {
"constEnums": [
"eStorageType",
"eBatchSendStatus",
"eBatchStoreStatus",
"CursorProcessResult",
"eStorageProviders",
"ValueQueryType"
]
}
}
}
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 = 135;
private readonly MAX_BUNDLE_SIZE = 135;
private readonly MAX_RAW_DEFLATE_SIZE = 54;
private readonly MAX_BUNDLE_DEFLATE_SIZE = 54;
private readonly MAX_RAW_SIZE = 136;
private readonly MAX_BUNDLE_SIZE = 136;
private readonly MAX_RAW_DEFLATE_SIZE = 55;
private readonly MAX_BUNDLE_DEFLATE_SIZE = 55;
private readonly rawFilePath = "../dist/es5/applicationinsights-web.min.js";
// Automatically updated by version scripts
private readonly currentVer = "3.0.7";
Expand Down
3 changes: 2 additions & 1 deletion AISKU/src/applicationinsights-web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ export {
RemoteDependencyData,
Trace,
DistributedTracingModes,
IRequestHeaders
IRequestHeaders,
EventPersistence
} from "@microsoft/applicationinsights-common";
export { Sender, ISenderConfig } from "@microsoft/applicationinsights-channel-js";
export { ApplicationInsights as ApplicationAnalytics, IAppInsightsInternal } from "@microsoft/applicationinsights-analytics-js";
Expand Down
34 changes: 31 additions & 3 deletions channels/1ds-post-js/src/HttpManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
import dynamicProto from "@microsoft/dynamicproto-js";
import {
EventSendType, FullVersionString, IAppInsightsCore, ICookieMgr, IDiagnosticLogger, IExtendedConfiguration, IPayloadData, IPerfEvent,
IUnloadHook, IXHROverride, OnCompleteCallback, SendPOSTFunction, SendRequestReason, TransportType, _eExtendedInternalMessageId,
_eInternalMessageId, _throwInternal, _warnToConsole, arrForEach, dateNow, doPerf, dumpObj, eLoggingSeverity, extend,
getCommonSchemaMetaData, getLocation, getNavigator, getTime, hasOwnProperty, isArray, isBeaconsSupported, isFetchSupported,
ITelemetryItem, IUnloadHook, IXHROverride, OnCompleteCallback, SendPOSTFunction, SendRequestReason, TransportType,
_eExtendedInternalMessageId, _eInternalMessageId, _throwInternal, _warnToConsole, arrForEach, dateNow, doPerf, dumpObj, eLoggingSeverity,
extend, getCommonSchemaMetaData, 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";
Expand Down Expand Up @@ -340,6 +340,15 @@ export class HttpManager {
}
};

_self.serializeOfflineEvt = (evt) => {
return _serializer.getEventBlob(evt);
}

_self.getOfflineRequestDetails = () => {
let payload = _serializer.createPayload(0, false, false, false, SendRequestReason.NormalSchedule, EventSendType.Batched);
return _buildRequestDetails(payload, _useHeaders);
}

// Special internal method to allow the DebugPlugin to hook embedded objects
function _getSenderInterface(transports: TransportType[], syncSupport: boolean): IInternalXhrOverride {
let transportType: TransportType = TransportType.NotSet;
Expand Down Expand Up @@ -1490,4 +1499,23 @@ export class HttpManager {
public sendSynchronousBatch(batch: EventBatch, sendType?: EventSendType, sendReason?: SendRequestReason) {
// @DynamicProtoStub - DO NOT add any code as this will be removed during packaging
}


/**
* Get Offline Serializer support
* @returns internal Offline Serializer object
*/
public serializeOfflineEvt(evt: ITelemetryItem | IPostTransmissionTelemetryItem): string {
// @DynamicProtoStub - DO NOT add any code as this will be removed during packaging
return null;
}

/**
* Get Offline request details
* @returnsrequest details
*/
public getOfflineRequestDetails(): IRequestUrlDetails {
// @DynamicProtoStub - DO NOT add any code as this will be removed during packaging
return null;
}
}
100 changes: 84 additions & 16 deletions channels/1ds-post-js/src/PostChannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
import dynamicProto from "@microsoft/dynamicproto-js";
import {
BaseTelemetryPlugin, EventLatencyValue, EventSendType, EventsDiscardedReason, IAppInsightsCore, IChannelControls, IConfigDefaults,
IExtendedConfiguration, IPlugin, IProcessTelemetryContext, IProcessTelemetryUnloadContext, ITelemetryItem, ITelemetryUnloadState,
IUnloadHook, NotificationManager, SendRequestReason, _eInternalMessageId, _throwInternal, addPageHideEventListener,
addPageShowEventListener, addPageUnloadEventListener, arrForEach, createProcessTelemetryContext, createUniqueNamespace, doPerf,
eLoggingSeverity, getWindow, isChromium, isGreaterThanZero, isNumber, mergeEvtNamespace, objForEachKey, onConfigChange, optimizeObject,
proxyFunctions, removePageHideEventListener, removePageShowEventListener, removePageUnloadEventListener, setProcessTelemetryTimings
IExtendedConfiguration, IInternalOfflineSupport, IPayloadData, IPlugin, IProcessTelemetryContext, IProcessTelemetryUnloadContext,
ITelemetryItem, ITelemetryUnloadState, IUnloadHook, NotificationManager, SendRequestReason, _eInternalMessageId, _throwInternal,
addPageHideEventListener, addPageShowEventListener, addPageUnloadEventListener, arrForEach, createProcessTelemetryContext,
createUniqueNamespace, doPerf, eLoggingSeverity, getWindow, isChromium, isGreaterThanZero, isNumber, mergeEvtNamespace, objForEachKey,
onConfigChange, optimizeObject, proxyFunctions, removePageHideEventListener, removePageShowEventListener, removePageUnloadEventListener,
setProcessTelemetryTimings
} from "@microsoft/1ds-core-js";
import { IPromise, createPromise } from "@nevware21/ts-async";
import { ITimerHandler, objDeepFreeze } from "@nevware21/ts-utils";
Expand All @@ -34,6 +35,7 @@ const MaxRequestRetriesBeforeBackoff = 1;
const MaxEventsLimitInMem = 10000;

const strEventsDiscarded = "eventsDiscarded";
const EMPTY_STR = "";

let undefValue = undefined;

Expand Down Expand Up @@ -231,6 +233,28 @@ export class PostChannel extends BaseTelemetryPlugin implements IChannelControls
_self.processNext(event, itemCtx);
};

_self.getOfflineSupport = () => {
let details = _httpManager.getOfflineRequestDetails();
return {
getUrl: () => {
return details.url
},
serialize: _serialize,
batch: _batch,
shouldProcess: (evt) => {
return !_disableTelemetry;
},
createPayload: (evt) => {
return {
urlString: details.url,
headers: details.hdrs,
data: evt
} as IPayloadData;
}
} as IInternalOfflineSupport;

};

_self._doTeardown = (unloadCtx?: IProcessTelemetryUnloadContext, unloadState?: ITelemetryUnloadState) => {
_releaseAllQueues(EventSendType.SendBeacon, SendRequestReason.Unload);
_isTeardownCalled = true;
Expand Down Expand Up @@ -259,6 +283,37 @@ export class PostChannel extends BaseTelemetryPlugin implements IChannelControls
};
}

function _batch(arr: string[]) {
let rlt = EMPTY_STR;

if (arr && arr.length) {
arrForEach(arr, (item) => {
if (rlt) {
rlt += "\n";
}
rlt += item;

});
}
return rlt;

}

function _serialize(event: ITelemetryItem) {

let rlt = EMPTY_STR;
try {
_cleanEvent(event);
rlt = _httpManager.serializeOfflineEvt(event);

} catch (e) {
// eslint-disable-next-line no-empty

}
return rlt;

}

// Moving event handlers out from the initialize closure so that any local variables can be garbage collected
function _handleUnloadEvents(evt: any) {
let theEvt = evt || getWindow().event; // IE 8 does not pass the event
Expand All @@ -277,17 +332,7 @@ export class PostChannel extends BaseTelemetryPlugin implements IChannelControls
_httpManager.setUnloading(_isPageUnloadTriggered);
}

function _addEventToQueues(event: IPostTransmissionTelemetryItem, append: boolean) {
// If send attempt field is undefined we should set it to 0.
if (!event.sendAttempt) {
event.sendAttempt = 0;
}
// Add default latency
if (!event.latency) {
event.latency = EventLatencyValue.Normal;
}

// Remove extra AI properties if present
function _cleanEvent(event: ITelemetryItem | IPostTransmissionTelemetryItem) {
if (event.ext && event.ext[STR_TRACE]) {
delete (event.ext[STR_TRACE]);
}
Expand All @@ -306,6 +351,20 @@ export class PostChannel extends BaseTelemetryPlugin implements IChannelControls
}
}

}

function _addEventToQueues(event: IPostTransmissionTelemetryItem, append: boolean) {
// If send attempt field is undefined we should set it to 0.
if (!event.sendAttempt) {
event.sendAttempt = 0;
}
// Add default latency
if (!event.latency) {
event.latency = EventLatencyValue.Normal;
}
_cleanEvent(event);


if (event.sync) {
// If the transmission is backed off then do not send synchronous events.
// We will convert these events to Real time latency instead.
Expand Down Expand Up @@ -1187,4 +1246,13 @@ export class PostChannel extends BaseTelemetryPlugin implements IChannelControls
public _clearBackOff() {
// @DynamicProtoStub - DO NOT add any code as this will be removed during packaging
}

/**
* Get Offline support
* @returns internal Offline support interface IInternalOfflineSupport
*/
public getOfflineSupport(): IInternalOfflineSupport {
// @DynamicProtoStub - DO NOT add any code as this will be removed during packaging
return null;
}
}
36 changes: 35 additions & 1 deletion channels/1ds-post-js/test/Unit/src/HttpManagerTest.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AITestClass } from "@microsoft/ai-test-framework";
import { HttpManager } from "../../../src/HttpManager";
import { AppInsightsCore, BaseTelemetryPlugin, EventSendType, IAppInsightsCore, IConfiguration, IExtendedConfiguration, IPlugin, IProcessTelemetryContext, ITelemetryItem, SendRequestReason, TransportType, isBeaconsSupported } from "@microsoft/1ds-core-js";
import { AppInsightsCore, BaseTelemetryPlugin, EventSendType, IAppInsightsCore, IConfiguration, IExtendedConfiguration, IPlugin, IProcessTelemetryContext, ITelemetryItem, SendRequestReason, TransportType, isBeaconsSupported} from "@microsoft/1ds-core-js";
import { PostChannel, IXHROverride, IPayloadData } from "../../../src/Index";
import { IPostTransmissionTelemetryItem, EventBatchNotificationReason, IChannelConfiguration } from "../../../src/DataModels";
import { EventBatch } from "../../../src/EventBatch";
Expand Down Expand Up @@ -196,6 +196,40 @@ export class HttpManagerTest extends AITestClass {
}
});

this.testCase({
name: "HttpManager: Offline Support",
useFakeTimers: true,
test: () => {
let core = this.core;
let postChannel = this.postManager;
core.config.extensionConfig = core.config.extensionConfig || {};
let postId = postChannel.identifier;

core.config.endpointUrl = "testEndpoint";

let manager: HttpManager = new HttpManager(500, 2, 1, {
requeue: _requeueNotification,
send: _sendNotification,
sent: _sentNotification,
drop: _dropNotification
});

manager.initialize(core.config, core, postChannel);
QUnit.assert.ok(manager.serializeOfflineEvt, "seralize function should exist");
let evt = this._createEvent();
evt.iKey = "testKey-123";
let evtStr = manager.serializeOfflineEvt(evt);
QUnit.assert.equal(evtStr, `{"name":"testEvent","iKey":"o:testKey","data":{"baseData":{}}}`,"Event should be serialized");

QUnit.assert.ok(manager.getOfflineRequestDetails, "request details function should exist");
let details = manager.getOfflineRequestDetails();
QUnit.assert.equal(details.url, "testEndpoint?cors=true&content-type=application/x-json-stream&w=0", "get expected Url");
QUnit.assert.ok(details.hdrs, "get headers Url");
QUnit.assert.ok(details.useHdrs, "should use headers");

}
});

this.testCase({
name: "payloadPreprocessor with override",
useFakeTimers: true,
Expand Down
43 changes: 43 additions & 0 deletions channels/1ds-post-js/test/Unit/src/PostChannelTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,49 @@ export class PostChannelTest extends AITestClass {
}
});

this.testCase({
name: "Post Channel: Offline Support",
useFakeTimers: true,
test: () => {
let config = this.config;
let core = this.core;
let postChannel = this.postChannel;
let postId = this.postChannel.identifier;
config.instrumentationKey = "ikey-123"
let event: IPostTransmissionTelemetryItem = {
name: "testEvent",
iKey: "testIkey-123"
};
core.initialize(config, [postChannel]);
let offlineSupport = this.postChannel.getOfflineSupport() as any;
QUnit.assert.ok(offlineSupport.serialize, "serialize exist");
let eventStr = offlineSupport.serialize(event);
let expectedStr = `{"name":"testEvent","iKey":"o:testIkey","data":{"baseData":{}}}`;
QUnit.assert.equal(eventStr, expectedStr, "get expected string");

QUnit.assert.ok(offlineSupport.batch, "batch should exit");
let batch = offlineSupport.batch([expectedStr, expectedStr]);
QUnit.assert.equal(batch, expectedStr + "\n" + expectedStr, "get expected batch");

QUnit.assert.ok(offlineSupport.shouldProcess, "should process should exit");
QUnit.assert.equal(offlineSupport.shouldProcess(event), true, "should process");

QUnit.assert.ok(offlineSupport.createPayload, "createPayload should exit");
let details = offlineSupport.createPayload("test");
QUnit.assert.equal(details.urlString, "https://testEndpoint?cors=true&content-type=application/x-json-stream&w=0", "get expected Url");
QUnit.assert.ok(details.headers, "get headers Url");
QUnit.assert.equal(details.data, "test", "get expected data");

this.core.config.extensionConfig = this.core.config.extensionConfig || {};
this.core.config.extensionConfig[postId].disableTelemetry = true;
this.clock.tick(1);
offlineSupport = this.postChannel.getOfflineSupport() as any;
QUnit.assert.equal(offlineSupport.shouldProcess(event), false, "should not process");

}
});


this.testCase({
name: "Send Sync Event with Specific type override",
test: () => {
Expand Down
Loading

0 comments on commit f9d5ec1

Please sign in to comment.