UNPKG

@aws-amplify/core

Version:
187 lines (184 loc) 6.7 kB
import { ConsoleLogger } from '../../../Logger/ConsoleLogger.mjs'; import '../../../utils/getClientInfo/getClientInfo.mjs'; import '../../../utils/retry/retry.mjs'; import '../../../types/errors.mjs'; import '@aws-crypto/sha256-js'; import '@smithy/util-hex-encoding'; import '../../../errors/errorHelpers.mjs'; import '../../../awsClients/pinpoint/base.mjs'; import { putEvents } from '../../../awsClients/pinpoint/putEvents.mjs'; import { isAppInForeground } from './isAppInForeground.mjs'; // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 const logger = new ConsoleLogger('PinpointEventBuffer'); const RETRYABLE_CODES = [429, 500]; const ACCEPTED_CODES = [202]; class PinpointEventBuffer { constructor(config) { this._interval = undefined; this._pause = false; this._flush = false; this._buffer = []; this._config = config; this._sendBatch = this._sendBatch.bind(this); this._startLoop(); } push(event) { if (this._buffer.length >= this._config.bufferSize) { logger.debug('Exceeded Pinpoint event buffer limits, event dropped.', { eventId: event.eventId, }); return; } this._buffer.push({ [event.eventId]: event }); } pause() { this._pause = true; } resume() { this._pause = false; } flush() { this._flush = true; } identityHasChanged(identityId) { return this._config.identityId !== identityId; } flushAll() { this._putEvents(this._buffer.splice(0, this._buffer.length)); } _startLoop() { if (this._interval) { clearInterval(this._interval); } const { flushInterval } = this._config; this._interval = setInterval(this._sendBatch, flushInterval); } _sendBatch() { const bufferLength = this._buffer.length; if (this._flush && !bufferLength && this._interval) { clearInterval(this._interval); } if (this._pause || !bufferLength || !isAppInForeground()) { return; } const { flushSize } = this._config; const batchSize = Math.min(flushSize, bufferLength); const bufferSubset = this._buffer.splice(0, batchSize); this._putEvents(bufferSubset); } async _putEvents(buffer) { const eventMap = this._bufferToMap(buffer); const batchEventParams = this._generateBatchEventParams(eventMap); try { const { credentials, region, userAgentValue } = this._config; const data = await putEvents({ credentials, region, userAgentValue, }, batchEventParams); this._processPutEventsSuccessResponse(data, eventMap); } catch (err) { this._handlePutEventsFailure(err, eventMap); } } _generateBatchEventParams(eventMap) { const batchItem = {}; Object.values(eventMap).forEach(item => { const { event, timestamp, endpointId, eventId, session } = item; const { name, attributes, metrics } = event; batchItem[endpointId] = { Endpoint: { ...batchItem[endpointId]?.Endpoint, }, Events: { ...batchItem[endpointId]?.Events, [eventId]: { EventType: name, Timestamp: new Date(timestamp).toISOString(), Attributes: attributes, Metrics: metrics, Session: session, }, }, }; }); return { ApplicationId: this._config.appId, EventsRequest: { BatchItem: batchItem, }, }; } _handlePutEventsFailure(err, eventMap) { logger.debug('putEvents call to Pinpoint failed.', err); const statusCode = err.$metadata && err.$metadata.httpStatusCode; if (RETRYABLE_CODES.includes(statusCode)) { const retryableEvents = Object.values(eventMap); this._retry(retryableEvents); } } _processPutEventsSuccessResponse(data, eventMap) { const { Results = {} } = data.EventsResponse ?? {}; const retryableEvents = []; Object.entries(Results).forEach(([_, endpointValues]) => { const responses = endpointValues.EventsItemResponse ?? {}; Object.entries(responses).forEach(([eventId, eventValues]) => { const eventObject = eventMap[eventId]; if (!eventObject) { return; } const { StatusCode, Message } = eventValues ?? {}; if (StatusCode && ACCEPTED_CODES.includes(StatusCode)) { return; } if (StatusCode && RETRYABLE_CODES.includes(StatusCode)) { retryableEvents.push(eventObject); return; } const { name } = eventObject.event; logger.warn('Pinpoint event failed to send.', { eventId, name, message: Message, }); }); }); if (retryableEvents.length) { this._retry(retryableEvents); } } _retry(retryableEvents) { // retryable events that haven't reached the resendLimit const eligibleEvents = []; retryableEvents.forEach((bufferedEvent) => { const { eventId } = bufferedEvent; const { name } = bufferedEvent.event; if (bufferedEvent.resendLimit-- > 0) { logger.debug('Resending event.', { eventId, name, remainingAttempts: bufferedEvent.resendLimit, }); eligibleEvents.push({ [eventId]: bufferedEvent }); return; } logger.debug('No retry attempts remaining for event.', { eventId, name, }); }); // add the events to the front of the buffer this._buffer.unshift(...eligibleEvents); } _bufferToMap(buffer) { return buffer.reduce((acc, curVal) => { const [[key, value]] = Object.entries(curVal); acc[key] = value; return acc; }, {}); } } export { PinpointEventBuffer }; //# sourceMappingURL=PinpointEventBuffer.mjs.map