diff --git a/channels/1ds-post-js/src/HttpManager.ts b/channels/1ds-post-js/src/HttpManager.ts index da5dedccd..3c86ff214 100644 --- a/channels/1ds-post-js/src/HttpManager.ts +++ b/channels/1ds-post-js/src/HttpManager.ts @@ -10,7 +10,7 @@ import { _IInternalXhrOverride, _ISendPostMgrConfig, _ISenderOnComplete, _eExtendedInternalMessageId, _eInternalMessageId, _getAllResponseHeaders, _throwInternal, _warnToConsole, arrForEach, dateNow, doPerf, dumpObj, eLoggingSeverity, extend, getCommonSchemaMetaData, getNavigator, getResponseText, getTime, hasOwnProperty, isBeaconsSupported, isFetchSupported, isNullOrUndefined, isReactNative, isUndefined, - isValueAssigned, objForEachKey, objKeys, onConfigChange, prependTransports, strUndefined + isValueAssigned, objForEachKey, objKeys, onConfigChange, optimizeObject, prependTransports, strUndefined } from "@microsoft/1ds-core-js"; import { arrAppend } from "@nevware21/ts-utils"; import { BatchNotificationAction, BatchNotificationActions } from "./BatchNotificationActions"; @@ -331,9 +331,55 @@ export class HttpManager { } _self.getOfflineRequestDetails = () => { + return null; + } + + _self.createOneDSPayload = (evts: ITelemetryItem[], optimize?: boolean) => { try { - let payload = _serializer && _serializer.createPayload(0, false, false, false, SendRequestReason.NormalSchedule, EventSendType.Batched); - return _buildRequestDetails(payload, _useHeaders); + // TODO: optimize + let theBatches: EventBatch[] = []; + // create a eventBatch for each event + arrForEach(evts, (evt) => { + if (optimize) { + evt = optimizeObject(evt) + } + let batch = EventBatch.create(evt.iKey, [evt]); + theBatches.push(batch); + }) + + let thePayload: ISerializedPayload = null; + + while (theBatches.length > 0 && _serializer) { + let theBatch = theBatches.shift(); + if (theBatch && theBatch.count() > 0) { + thePayload = thePayload || _serializer.createPayload(0, false, false, false, SendRequestReason.NormalSchedule, EventSendType.Batched); + _serializer.appendPayload(thePayload, theBatch, maxEventsPerBatch) + } + } + + let requestDetails = _buildRequestDetails(thePayload, _useHeaders); + + let payloadData: IPayloadData = { + data: thePayload.payloadBlob, + urlString: requestDetails.url, + headers: requestDetails.hdrs, + timeout: _xhrTimeout, + disableXhrSync: _disableXhrSync, + disableFetchKeepAlive: _disableFetchKeepAlive + }; + + // Only automatically add the following headers if already sending headers and we are not attempting to avoid an options call + if (_useHeaders) { + if (!_hasHeader(payloadData.headers, STR_CACHE_CONTROL)) { + payloadData.headers[STR_CACHE_CONTROL] = DEFAULT_CACHE_CONTROL; + } + + if (!_hasHeader(payloadData.headers, STR_CONTENT_TYPE_HEADER)) { + payloadData.headers[STR_CONTENT_TYPE_HEADER] = DEFAULT_CONTENT_TYPE; + } + } + + return payloadData; } catch (e) { // eslint-disable-next-line no-empty @@ -342,6 +388,8 @@ export class HttpManager { return null; } + + // Special internal method to allow the DebugPlugin to hook embedded objects function _getSenderInterface(transports: TransportType[], syncSupport: boolean): _IInternalXhrOverride { try { @@ -1355,4 +1403,14 @@ export class HttpManager { // @DynamicProtoStub - DO NOT add any code as this will be removed during packaging return null; } + + /** + * Create payload data + * @param evts telemetry events + * @returns payload + */ + public createOneDSPayload(evts?: ITelemetryItem[], optimize?: boolean): IPayloadData { + // @DynamicProtoStub - DO NOT add any code as this will be removed during packaging + return null; + } } diff --git a/channels/1ds-post-js/src/PostChannel.ts b/channels/1ds-post-js/src/PostChannel.ts index 2ad6015ec..2760af12d 100644 --- a/channels/1ds-post-js/src/PostChannel.ts +++ b/channels/1ds-post-js/src/PostChannel.ts @@ -236,7 +236,7 @@ export class PostChannel extends BaseTelemetryPlugin implements IChannelControls _self.getOfflineSupport = () => { try { let details = _httpManager && _httpManager.getOfflineRequestDetails(); - if (details) { + if (_httpManager) { return { getUrl: () => { return details.url @@ -247,11 +247,13 @@ export class PostChannel extends BaseTelemetryPlugin implements IChannelControls return !_disableTelemetry; }, createPayload: (evt) => { - return { - urlString: details.url, - headers: details.hdrs, - data: evt - } as IPayloadData; + return null; + }, + createOneDSPayload: (evts: ITelemetryItem[]) => { + if (_httpManager.createOneDSPayload) { + return _httpManager.createOneDSPayload(evts, _optimizeObject); + } + } } as IInternalOfflineSupport; diff --git a/channels/1ds-post-js/test/Unit/src/HttpManagerTest.ts b/channels/1ds-post-js/test/Unit/src/HttpManagerTest.ts index 0df51376d..4545fff52 100644 --- a/channels/1ds-post-js/test/Unit/src/HttpManagerTest.ts +++ b/channels/1ds-post-js/test/Unit/src/HttpManagerTest.ts @@ -222,10 +222,30 @@ export class HttpManagerTest extends AITestClass { 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"); + QUnit.assert.equal(manager.getOfflineRequestDetails(), null, "request details function should return null for 1ds"); + let details = manager.createOneDSPayload([evt]); + let headers = details.headers || {}; + let apiKey = headers["apikey"]; + QUnit.assert.equal(apiKey, "testKey-123", "should get expected api key"); + QUnit.assert.equal(details.data, `{"name":"testEvent","iKey":"o:testKey","data":{"baseData":{}}}`, "should return expected data"); + + let evt1 = this._createEvent(); + evt1.iKey = "testKey-12345"; + evt1.name = "testEvent1"; + details = manager.createOneDSPayload([evt, evt1]); + headers = details.headers || {}; + apiKey = headers["apikey"]; + QUnit.assert.equal(apiKey, "testKey-123,testKey-12345", "should get expected api keys test1"); + QUnit.assert.equal(details.data, `{"name":"testEvent","iKey":"o:testKey","data":{"baseData":{}}}\n{"name":"testEvent1","iKey":"o:testKey","data":{"baseData":{}}}`, "should return expected data test1"); + + let evt2 = this._createEvent(); + evt2.iKey = "testKey-123"; + evt2.name = "testEvent2"; + details = manager.createOneDSPayload([evt, evt2]); + headers = details.headers || {}; + apiKey = headers["apikey"]; + QUnit.assert.equal(apiKey, "testKey-123", "should get expected api keys test2"); + QUnit.assert.equal(details.data, `{"name":"testEvent","iKey":"o:testKey","data":{"baseData":{}}}\n{"name":"testEvent2","iKey":"o:testKey","data":{"baseData":{}}}`, "should return expected data test2"); } }); diff --git a/channels/1ds-post-js/test/Unit/src/PostChannelTest.ts b/channels/1ds-post-js/test/Unit/src/PostChannelTest.ts index 30ab76194..195e9c5ed 100644 --- a/channels/1ds-post-js/test/Unit/src/PostChannelTest.ts +++ b/channels/1ds-post-js/test/Unit/src/PostChannelTest.ts @@ -311,10 +311,11 @@ export class PostChannelTest extends AITestClass { 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(offlineSupport.createPayload("test"), null, "createPayload should return null now"); + let details = offlineSupport.createOneDSPayload([event]); 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"); + QUnit.assert.equal(details.data, expectedStr, "get expected data"); this.core.config.extensionConfig = this.core.config.extensionConfig || {}; this.core.config.extensionConfig[postId].disableTelemetry = true; diff --git a/channels/offline-channel-js/src/OfflineChannel.ts b/channels/offline-channel-js/src/OfflineChannel.ts index c3faec0ab..28dba8828 100644 --- a/channels/offline-channel-js/src/OfflineChannel.ts +++ b/channels/offline-channel-js/src/OfflineChannel.ts @@ -347,8 +347,19 @@ export class OfflineChannel extends BaseTelemetryPlugin implements IChannelContr let sentItems = evts.slice(0, idx + 1); _inMemoBatch = _inMemoBatch.createNew(_endpoint, inMemo.getItems().slice(idx + 1), _evtsLimitInMemo); + + let payloadData: IStorageTelemetryItem = null; + if (_offineSupport && _offineSupport.createOneDSPayload) { + payloadData = _offineSupport.createOneDSPayload(sentItems); + if (payloadData) { + payloadData.criticalCnt = criticalCnt; + } + + } else { + payloadData = _constructPayloadData(payloadArr, criticalCnt); + } - let payloadData = _constructPayloadData(payloadArr, criticalCnt); + let callback: OfflineBatchStoreCallback = (res) => { if (!res || !res.state) { return null; diff --git a/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IChannelControls.ts b/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IChannelControls.ts index 4165dbe4c..68632534b 100644 --- a/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IChannelControls.ts +++ b/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IChannelControls.ts @@ -47,6 +47,13 @@ export interface IInternalOfflineSupport { */ shouldProcess?: (evt: ITelemetryItem) => boolean; + /** + * Create 1ds payload data + * @param evts ITelemetryItems + * @returns IPayloadData + */ + createOneDSPayload?: (evts: ITelemetryItem[]) => IPayloadData; + } /**