@firebase/firestore
Version:
This is the [Cloud Firestore](https://firebase.google.com/docs/firestore/) component of the [Firebase JS SDK](https://www.npmjs.com/package/firebase).
287 lines (286 loc) • 12.7 kB
TypeScript
/**
* @license
* Copyright 2017 Google Inc.
*
* 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.
*/
import { CredentialsProvider, Token } from '../api/credentials';
import { SnapshotVersion } from '../core/snapshot_version';
import { ProtoByteString, TargetId } from '../core/types';
import { TargetData } from '../local/target_data';
import { Mutation, MutationResult } from '../model/mutation';
import * as api from '../protos/firestore_proto_api';
import { AsyncQueue, TimerId } from '../util/async_queue';
import { FirestoreError } from '../util/error';
import { ExponentialBackoff } from './backoff';
import { Connection, Stream } from './connection';
import { JsonProtoSerializer } from './serializer';
import { WatchChange } from './watch_change';
export interface WriteRequest extends api.WriteRequest {
database?: string;
}
/**
* Provides a common interface that is shared by the listeners for stream
* events by the concrete implementation classes.
*/
export interface PersistentStreamListener {
/**
* Called after the stream was established and can accept outgoing
* messages
*/
onOpen: () => Promise<void>;
/**
* Called after the stream has closed. If there was an error, the
* FirestoreError will be set.
*/
onClose: (err?: FirestoreError) => Promise<void>;
}
/**
* A PersistentStream is an abstract base class that represents a streaming RPC
* to the Firestore backend. It's built on top of the connections own support
* for streaming RPCs, and adds several critical features for our clients:
*
* - Exponential backoff on failure
* - Authentication via CredentialsProvider
* - Dispatching all callbacks into the shared worker queue
* - Closing idle streams after 60 seconds of inactivity
*
* Subclasses of PersistentStream implement serialization of models to and
* from the JSON representation of the protocol buffers for a specific
* streaming RPC.
*
* ## Starting and Stopping
*
* Streaming RPCs are stateful and need to be start()ed before messages can
* be sent and received. The PersistentStream will call the onOpen() function
* of the listener once the stream is ready to accept requests.
*
* Should a start() fail, PersistentStream will call the registered onClose()
* listener with a FirestoreError indicating what went wrong.
*
* A PersistentStream can be started and stopped repeatedly.
*
* Generic types:
* SendType: The type of the outgoing message of the underlying
* connection stream
* ReceiveType: The type of the incoming message of the underlying
* connection stream
* ListenerType: The type of the listener that will be used for callbacks
*/
export declare abstract class PersistentStream<SendType, ReceiveType, ListenerType extends PersistentStreamListener> {
private queue;
private idleTimerId;
protected connection: Connection;
private credentialsProvider;
protected listener: ListenerType;
private state;
/**
* A close count that's incremented every time the stream is closed; used by
* getCloseGuardedDispatcher() to invalidate callbacks that happen after
* close.
*/
private closeCount;
private idleTimer;
private stream;
protected backoff: ExponentialBackoff;
constructor(queue: AsyncQueue, connectionTimerId: TimerId, idleTimerId: TimerId, connection: Connection, credentialsProvider: CredentialsProvider, listener: ListenerType);
/**
* Returns true if start() has been called and no error has occurred. True
* indicates the stream is open or in the process of opening (which
* encompasses respecting backoff, getting auth tokens, and starting the
* actual RPC). Use isOpen() to determine if the stream is open and ready for
* outbound requests.
*/
isStarted(): boolean;
/**
* Returns true if the underlying RPC is open (the onOpen() listener has been
* called) and the stream is ready for outbound requests.
*/
isOpen(): boolean;
/**
* Starts the RPC. Only allowed if isStarted() returns false. The stream is
* not immediately ready for use: onOpen() will be invoked when the RPC is
* ready for outbound requests, at which point isOpen() will return true.
*
* When start returns, isStarted() will return true.
*/
start(): void;
/**
* Stops the RPC. This call is idempotent and allowed regardless of the
* current isStarted() state.
*
* When stop returns, isStarted() and isOpen() will both return false.
*/
stop(): Promise<void>;
/**
* After an error the stream will usually back off on the next attempt to
* start it. If the error warrants an immediate restart of the stream, the
* sender can use this to indicate that the receiver should not back off.
*
* Each error will call the onClose() listener. That function can decide to
* inhibit backoff if required.
*/
inhibitBackoff(): void;
/**
* Marks this stream as idle. If no further actions are performed on the
* stream for one minute, the stream will automatically close itself and
* notify the stream's onClose() handler with Status.OK. The stream will then
* be in a !isStarted() state, requiring the caller to start the stream again
* before further use.
*
* Only streams that are in state 'Open' can be marked idle, as all other
* states imply pending network operations.
*/
markIdle(): void;
/** Sends a message to the underlying stream. */
protected sendRequest(msg: SendType): void;
/** Called by the idle timer when the stream should close due to inactivity. */
private handleIdleCloseTimer;
/** Marks the stream as active again. */
private cancelIdleCheck;
/**
* Closes the stream and cleans up as necessary:
*
* * closes the underlying GRPC stream;
* * calls the onClose handler with the given 'error';
* * sets internal stream state to 'finalState';
* * adjusts the backoff timer based on the error
*
* A new stream can be opened by calling start().
*
* @param finalState the intended state of the stream after closing.
* @param error the error the connection was closed with.
*/
private close;
/**
* Can be overridden to perform additional cleanup before the stream is closed.
* Calling super.tearDown() is not required.
*/
protected tearDown(): void;
/**
* Used by subclasses to start the concrete RPC and return the underlying
* connection stream.
*/
protected abstract startRpc(token: Token | null): Stream<SendType, ReceiveType>;
/**
* Called after the stream has received a message. The function will be
* called on the right queue and must return a Promise.
* @param message The message received from the stream.
*/
protected abstract onMessage(message: ReceiveType): Promise<void>;
private auth;
private startStream;
private performBackoff;
handleStreamClose(error?: FirestoreError): Promise<void>;
/**
* Returns a "dispatcher" function that dispatches operations onto the
* AsyncQueue but only runs them if closeCount remains unchanged. This allows
* us to turn auth / stream callbacks into no-ops if the stream is closed /
* re-opened, etc.
*/
private getCloseGuardedDispatcher;
}
/** Listener for the PersistentWatchStream */
export interface WatchStreamListener extends PersistentStreamListener {
/**
* Called on a watchChange. The snapshot parameter will be MIN if the watch
* change did not have a snapshot associated with it.
*/
onWatchChange: (watchChange: WatchChange, snapshot: SnapshotVersion) => Promise<void>;
}
/**
* A PersistentStream that implements the Listen RPC.
*
* Once the Listen stream has called the onOpen() listener, any number of
* listen() and unlisten() calls can be made to control what changes will be
* sent from the server for ListenResponses.
*/
export declare class PersistentListenStream extends PersistentStream<api.ListenRequest, api.ListenResponse, WatchStreamListener> {
private serializer;
constructor(queue: AsyncQueue, connection: Connection, credentials: CredentialsProvider, serializer: JsonProtoSerializer, listener: WatchStreamListener);
protected startRpc(token: Token | null): Stream<api.ListenRequest, api.ListenResponse>;
protected onMessage(watchChangeProto: api.ListenResponse): Promise<void>;
/**
* Registers interest in the results of the given target. If the target
* includes a resumeToken it will be included in the request. Results that
* affect the target will be streamed back as WatchChange messages that
* reference the targetId.
*/
watch(targetData: TargetData): void;
/**
* Unregisters interest in the results of the target associated with the
* given targetId.
*/
unwatch(targetId: TargetId): void;
}
/** Listener for the PersistentWriteStream */
export interface WriteStreamListener extends PersistentStreamListener {
/**
* Called by the PersistentWriteStream upon a successful handshake response
* from the server, which is the receiver's cue to send any pending writes.
*/
onHandshakeComplete: () => Promise<void>;
/**
* Called by the PersistentWriteStream upon receiving a StreamingWriteResponse
* from the server that contains a mutation result.
*/
onMutationResult: (commitVersion: SnapshotVersion, results: MutationResult[]) => Promise<void>;
}
/**
* A Stream that implements the Write RPC.
*
* The Write RPC requires the caller to maintain special streamToken
* state in between calls, to help the server understand which responses the
* client has processed by the time the next request is made. Every response
* will contain a streamToken; this value must be passed to the next
* request.
*
* After calling start() on this stream, the next request must be a handshake,
* containing whatever streamToken is on hand. Once a response to this
* request is received, all pending mutations may be submitted. When
* submitting multiple batches of mutations at the same time, it's
* okay to use the same streamToken for the calls to writeMutations.
*
* TODO(b/33271235): Use proto types
*/
export declare class PersistentWriteStream extends PersistentStream<api.WriteRequest, api.WriteResponse, WriteStreamListener> {
private serializer;
private handshakeComplete_;
constructor(queue: AsyncQueue, connection: Connection, credentials: CredentialsProvider, serializer: JsonProtoSerializer, listener: WriteStreamListener);
/**
* The last received stream token from the server, used to acknowledge which
* responses the client has processed. Stream tokens are opaque checkpoint
* markers whose only real value is their inclusion in the next request.
*
* PersistentWriteStream manages propagating this value from responses to the
* next request.
*/
lastStreamToken: ProtoByteString;
/**
* Tracks whether or not a handshake has been successfully exchanged and
* the stream is ready to accept mutations.
*/
get handshakeComplete(): boolean;
start(): void;
protected tearDown(): void;
protected startRpc(token: Token | null): Stream<api.WriteRequest, api.WriteResponse>;
protected onMessage(responseProto: api.WriteResponse): Promise<void>;
/**
* Sends an initial streamToken to the server, performing the handshake
* required to make the StreamingWrite RPC work. Subsequent
* calls should wait until onHandshakeComplete was called.
*/
writeHandshake(): void;
/** Sends a group of mutations to the Firestore backend to apply. */
writeMutations(mutations: Mutation[]): void;
}