UNPKG

@ibm-cloud/cloudant

Version:
190 lines 8.71 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ChangesFollower = void 0; /** * © Copyright IBM Corporation 2022, 2025. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const node_stream_1 = require("node:stream"); const stream_1 = require("./stream"); const changesParamsHelper_1 = require("./changesParamsHelper"); const changesResultIterator_1 = require("./changesResultIterator"); /** * A helper for using the changes feed. * * There are two modes of operation: * * {@link startOneOff} to fetch the changes from the supplied since sequence * until there are no further pending changes. * * {@link start} to fetch the changes from the supplied since sequence and * then continuing to listen indefinitely for further new changes. * * The starting sequence ID can be changed for either mode by using * {@link CloudantV1.PostChangesParams.since}. By default when using: * * {@link startOneOff} the feed will start from the beginning. * * {@link start} the feed will start from "now". * * In either mode the {@link Stream} of changes can be terminated early by calling * {@link stop}. * * By default {@link ChangesFollower} will suppress transient errors indefinitely and * endeavour to run to completion or listen forever. For applications where that * behaviour is not desirable an alternate constructor is available where a * {@link errorTolerance} may be specified to limit the time since the last successful * response that transient errors will be suppressed. * * It should be noted that errors considered terminal, for example, the database not * existing or invalid credentials are never suppressed and will throw an exception * immediately. * * The {@link CloudantV1.PostChangesParams} model of changes feed options is used to configure * the behaviour of the {@link ChangesFollower}. However, a subset of the options are * invalid as they are configured internally by the implementation and will cause * an {@link Error} to be thrown if supplied. These invalid * options are: * * {@link CloudantV1.PostChangesParams.descending} * * {@link CloudantV1.PostChangesParams.feed} * * {@link CloudantV1.PostChangesParams.heartbeat} * * {@link CloudantV1.PostChangesParams.lastEventId} * (use {@link CloudantV1.PostChangesParams.since} instead) * * {@link CloudantV1.PostChangesParams.timeout} * * * Only the value of `_selector` is permitted for * the {@link CloudantV1.PostChangesParams.filter} option. * Selector based filters perform better than JS based filters and using one * of the alternative JS based * filter types will cause {@link ChangesFollower} to throw an {@link Error}. * * It should also be noted that the {@link CloudantV1.PostChangesParams.limit} * parameter will truncate the stream at the given number of changes in either * operating mode. * * The {@link ChangesFollower} requires the {@link CloudantV1} client to have * HTTP call and read timeouts of at least 1 minute. The default client * configuration has sufficiently long timeouts. */ class ChangesFollower { // Initialization fields client; params; errorTolerance; limit; changesResultIterator; /** * Create a new {@link ChangesFollower} using the supplied client and params that * suppress transient errors and retry for as long as the given `errorTolerance` duration. * * @param client - {@link CloudantV1} client instance to use to make requests * @param params - Changes feed params * @param errorTolerance - the duration to suppress errors, measured from the previous * successful request. Use `0` to disable error suppression and terminate this {@link ChangesFollower} * on any failed request. * * @throws {Error} if there are invalid params */ constructor(client, params, errorTolerance) { // Validate supplied params changesParamsHelper_1.ChangesParamsHelper.validateParams(params); this.limit = params.limit; // Setup with the required defaults and merges of user params this.params = changesParamsHelper_1.ChangesParamsHelper.cloneParams(params); this.client = client; if (errorTolerance < 0) { throw new Error('Error tolerance duration must not be negative.'); } if (errorTolerance === undefined || errorTolerance === null) { this.errorTolerance = Number.MAX_VALUE; } else { this.errorTolerance = errorTolerance; } // Check the timeout is suitable const readTimeout = this.client.getTimeout(); if (readTimeout > 0 && readTimeout < changesParamsHelper_1.ChangesParamsHelper.MIN_CLIENT_TIMEOUT) { throw new Error(`To use ChangesFollower the client read timeout must be at least ${changesParamsHelper_1.ChangesParamsHelper.MIN_CLIENT_TIMEOUT} ms. The client read timeout is ${readTimeout} ms.`); } } /** * Return all available changes and keep listening for new changes until reaching an end condition. * * The end conditions are: * * a terminal error (e.g. unauthorized client). * * transient errors occur for longer than the error suppression duration. * * the number of changes received reaches the limit specified in the {@link CloudantV1.PostChangesParams} used to instantiate this {@link ChangesFollower}. * * {@link stop} is called. * * The same change may be received more than once. * * @return {Stream} at least one {@link ChangesResultItem} per change * @throws {Error} if: * * {@link start} or {@link startOneOff} was already called. * * a terminal error or unsuppressed transient error is received from the service when fetching changes. */ start() { return this.run(changesParamsHelper_1.Mode.LISTEN); } /** * Return all available changes until there are no further changes pending or * reaching an end condition. * * The end conditions are: * * a terminal error (e.g. unauthorized client). * * transient errors occur for longer than the error suppression duration. * * the number of changes received reaches the limit specified in the {@link CloudantV1.PostChangesParams} used to instantiate this {@link ChangesFollower}. * * {@link stop} is called. * * The same change may be received more than once. * * @return {Stream} at least one {@link ChangesResultItem} per change * @throws {Error} if: * * {@link start} or {@link startOneOff} was already called. * * a terminal error or unsuppressed transient error is received from the service when fetching changes. */ startOneOff() { return this.run(changesParamsHelper_1.Mode.FINITE); } /** * Stop this {@link ChangesFollower}. * * @throws {Error} if {@link start} * or {@link startOneOff} was not called first */ stop() { if (this.changesResultIterator) { this.changesResultIterator.return(); } else { throw new Error('Cannot stop a feed that is not running.'); } } /** * * @param mode the mode in which to run the ChangesFollower * @private */ run(mode) { if (!this.changesResultIterator) { return this.createChangesResultItemsStream(mode); } throw new Error('Cannot start a feed that has already started.'); } createChangesResultItemsStream(mode) { this.changesResultIterator = new changesResultIterator_1.ChangesResultIterableIterator(this.client, changesParamsHelper_1.ChangesParamsHelper.cloneParams(this.params, mode), mode, this.errorTolerance); const resultsIterator = node_stream_1.Readable.from((0, node_stream_1.pipeline)(node_stream_1.Readable.from(this.changesResultIterator), new stream_1.Stream(), () => { })).flatMap((item) => item.results); return (0, node_stream_1.pipeline)(resultsIterator, new stream_1.Stream(), () => { }); } } exports.ChangesFollower = ChangesFollower; //# sourceMappingURL=changesFollower.js.map