box-node-sdk
Version:
Official SDK for Box Platform APIs
178 lines • 7.82 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EventStream = void 0;
const errors_1 = require("./errors");
const utilsNode_1 = require("../internal/utilsNode");
var RealtimeServerEventValue;
(function (RealtimeServerEventValue) {
RealtimeServerEventValue["NEW_CHANGE"] = "new_change";
RealtimeServerEventValue["RECONNECT"] = "reconnect";
})(RealtimeServerEventValue || (RealtimeServerEventValue = {}));
/**
* EventStream is a readable stream that fetches events from the Box API.
* It uses long polling to receive real-time updates.
* This class is designed to be used with Node.js streams.
*
* @param {EventsManager} options.eventsManager - The EventsManager instance which provides relevant methods to fetch events.
* @param {GetEventsQueryParams} options.queryParams - The query parameters to use for fetching events.
* @param {GetEventsHeadersInput} options.headersInput - The headers to include in the request.
*/
class EventStream extends utilsNode_1.ByteStream {
constructor(options) {
super({
objectMode: true,
});
this._longPollingRetries = 0;
this._started = false;
this._deduplicationFilterSize = 1000;
this._dedupHash = new Map();
this._eventsManager = options.eventsManager;
this._streamPosition = options.queryParams.streamPosition || 'now';
this._queryParams = options.queryParams;
this._headersInput = options.headersInput;
this._abortController = new AbortController();
this._dedupHash = new Map();
}
_read(size) {
if (this.destroyed) {
return;
}
if (!this._started) {
this._started = true;
this.fetchEvents();
}
}
_destroy(error, callback) {
var _a;
(_a = this._abortController) === null || _a === void 0 ? void 0 : _a.abort('Stream destroyed');
if (!error) {
this.push(null);
}
callback(error);
}
getLongPollInfo() {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
if (this.destroyed) {
return;
}
try {
const info = yield this._eventsManager.getEventsWithLongPolling(undefined, (_a = this._abortController) === null || _a === void 0 ? void 0 : _a.signal);
const server = ((_b = info.entries) === null || _b === void 0 ? void 0 : _b.find((entry) => entry.type === 'realtime_server')) ||
undefined;
if (!server) {
throw new errors_1.BoxSdkError({
message: 'No realtime server found in the response.',
});
}
this._longPollingInfo = server;
this._longPollingRetries = 0;
return this.doLongPoll();
}
catch (error) {
if (error.name !== 'AbortError') {
this.emit('error', error);
return this.getLongPollInfo();
}
}
});
}
doLongPoll() {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c;
if (this.destroyed) {
return;
}
try {
if (!this._longPollingInfo ||
this._longPollingRetries >
parseInt(((_a = this._longPollingInfo) === null || _a === void 0 ? void 0 : _a.maxRetries) || '10', 10)) {
return this.getLongPollInfo();
}
this._longPollingRetries++;
const longPollUrl = (_b = this._longPollingInfo) === null || _b === void 0 ? void 0 : _b.url;
const longPollWithStreamPosition = `${longPollUrl}${longPollUrl.includes('?') ? '&' : '?'}stream_position=${this._streamPosition}`;
const response = yield this._eventsManager.networkSession.networkClient.fetch({
url: longPollWithStreamPosition,
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
responseFormat: 'json',
auth: this._eventsManager.auth,
networkSession: this._eventsManager.networkSession,
cancellationToken: (_c = this._abortController) === null || _c === void 0 ? void 0 : _c.signal,
});
if (this.destroyed) {
return;
}
if (response.data) {
const message = response.data;
if (message.message === RealtimeServerEventValue.NEW_CHANGE) {
return this.fetchEvents();
}
else if (message.message === RealtimeServerEventValue.RECONNECT) {
return this.getLongPollInfo();
}
return this.doLongPoll();
}
}
catch (error) {
if (error.name !== 'AbortError') {
this.emit('error', error);
this.doLongPoll();
}
}
});
}
fetchEvents() {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
if (this.destroyed) {
return;
}
try {
const events = yield this._eventsManager.getEvents(Object.assign(Object.assign({}, this._queryParams), {
streamPosition: this._streamPosition,
}), this._headersInput, (_a = this._abortController) === null || _a === void 0 ? void 0 : _a.signal);
this._streamPosition = ((_b = events.nextStreamPosition) === null || _b === void 0 ? void 0 : _b.toString()) || 'now';
if (events.entries) {
for (const entry of events.entries) {
if (entry.eventId) {
if (this._dedupHash.has(entry.eventId)) {
continue;
}
this._dedupHash.set(entry.eventId, true);
}
this.push(entry);
}
if (this._dedupHash.size >= this._deduplicationFilterSize) {
for (const [key] of this._dedupHash) {
if (!events.entries.some((entry) => entry.eventId === key)) {
this._dedupHash.delete(key);
}
}
}
}
return this.doLongPoll();
}
catch (error) {
if (error.name !== 'AbortError') {
this.emit('error', error);
this.fetchEvents();
}
}
});
}
}
exports.EventStream = EventStream;
//# sourceMappingURL=eventStream.js.map