Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the type of the scope a generic #38

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 33 additions & 29 deletions src/nodefire.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {Simulator} from 'firefight';
import {Agent} from 'http';

export type InterceptOperationsCallback = (
op: {ref: NodeFire, method: string, args: any[]},
op: {ref: NodeFire<Scope>, method: string, args: any[]},
options: any
) => Promise<void> | void;

Expand All @@ -30,6 +30,8 @@ export interface Scope {
[key: string]: any;
}

type EmptyScope = {}; // eslint-disable-line @typescript-eslint/ban-types

export interface NodeFireError extends Error {
inputValues?: any;
timeout?: number;
Expand All @@ -56,13 +58,13 @@ declare module '@firebase/database-types' {
* standard option is `timeout`, which will cause an operation to time out after the given number of
* milliseconds. Other operation-specific options are described in their respective doc comments.
*/
export default class NodeFire {
export default class NodeFire<TScope extends Scope = EmptyScope> {
/**
* Flag that indicates whether to log transactions and the number of tries needed.
*/
static LOG_TRANSACTIONS = false;
$ref: admin.database.Reference | admin.database.Query;
$scope: Scope;
$scope: TScope;
private _path?: string;

/**
Expand All @@ -71,7 +73,7 @@ export default class NodeFire {
* @param ref A fully authenticated Firebase Admin reference or query.
* @param scope Optional dictionary that will be used for interpolating paths.
*/
constructor(ref: admin.database.Reference | admin.database.Query, scope?: Scope) {
constructor(ref: admin.database.Reference | admin.database.Query, scope?: TScope) {
const refIsNonNullObject = typeof ref === 'object' && ref !== null;
if (!refIsNonNullObject || typeof ref.ref !== 'object' ||
typeof ref.ref.transaction !== 'function') {
Expand All @@ -80,7 +82,7 @@ export default class NodeFire {
but got "${ref}".`
);
}
this.$scope = scope || {};
this.$scope = scope || {} as TScope;
this.$ref = ref;
this._path = undefined; // initialized lazily

Expand Down Expand Up @@ -131,7 +133,7 @@ export default class NodeFire {
* Returns a NodeFire reference at the same location as this query or reference.
* @return A NodeFire reference at the same location as this query or reference.
*/
get ref(): NodeFire {
get ref(): NodeFire<TScope> {
if (this.$ref.isEqual(this.$ref.ref)) return this;
return new NodeFire(this.$ref.ref, this.$scope);
}
Expand All @@ -140,7 +142,7 @@ export default class NodeFire {
* Returns a NodeFire reference to the root of the database.
* @return {NodeFire} The root reference of the database.
*/
get root(): NodeFire {
get root(): NodeFire<TScope> {
if (this.$ref.isEqual(this.$ref.ref.root)) return this;
return new NodeFire(this.$ref.ref.root, this.$scope);
}
Expand All @@ -150,7 +152,7 @@ export default class NodeFire {
* reference is `null`.
* @return {NodeFire|null} The parent location of this reference.
*/
get parent(): NodeFire | null {
get parent(): NodeFire<TScope> | null {
if (this.$ref.ref.parent === null) return null;
return new NodeFire(this.$ref.ref.parent, this.$scope);
}
Expand Down Expand Up @@ -213,7 +215,7 @@ export default class NodeFire {
* @param {Nodefire} otherRef Another NodeFire instance against which to compare.
* @return {boolean}
*/
isEqual(otherRef: NodeFire): boolean {
isEqual(otherRef: NodeFire<Scope>): boolean {
return this.$ref.isEqual(otherRef.$ref);
}

Expand All @@ -231,7 +233,7 @@ export default class NodeFire {
* precedence over) the one carried by this NodeFire object.
* @return A new NodeFire object with the same reference and new scope.
*/
scope(scope: Scope): NodeFire {
scope<TSource extends Scope>(scope: TSource): NodeFire<TScope & TSource> {
return new NodeFire(this.$ref, _.assign(_.clone(this.$scope), scope));
}

Expand All @@ -244,7 +246,9 @@ export default class NodeFire {
* scope.
* @return {NodeFire} A new NodeFire object on the child reference, and with the augmented scope.
*/
child(path: string, scope?: Scope): NodeFire {
child<TSource extends Scope = EmptyScope>(
path: string, scope?: TSource):
NodeFire<TScope & TSource> {
const child = this.scope(scope);
return new NodeFire(this.$ref.ref.child(child.interpolate(path)), child.$scope);
}
Expand Down Expand Up @@ -345,7 +349,7 @@ export default class NodeFire {
* @return A promise that is resolved to a new NodeFire object that refers to the newly
* pushed value (with the same scope as this object), or rejected with an error.
*/
push(value: Value, options?: { timeout?: number }): Promise<NodeFire> {
push(value: Value, options?: { timeout?: number }): Promise<NodeFire<TScope>> {
if (value === undefined || value === null) {
return Promise.resolve(new NodeFire(this.$ref.ref.push(), this.$scope));
}
Expand Down Expand Up @@ -709,35 +713,35 @@ export default class NodeFire {
});
}

orderByChild(path: string): NodeFire {
orderByChild(path: string): NodeFire<TScope> {
return new NodeFire(this.$ref.orderByChild(path), this.$scope);
}

equalTo(value: PrimitiveValue): NodeFire {
equalTo(value: PrimitiveValue): NodeFire<TScope> {
return new NodeFire(this.$ref.equalTo(value), this.$scope);
}

limitToFirst(limit: number): NodeFire {
limitToFirst(limit: number): NodeFire<TScope> {
return new NodeFire(this.$ref.limitToFirst(limit), this.$scope);
}

limitToLast(limit: number): NodeFire {
limitToLast(limit: number): NodeFire<TScope> {
return new NodeFire(this.$ref.limitToLast(limit), this.$scope);
}

startAt(value: PrimitiveValue, key?: string): NodeFire {
startAt(value: PrimitiveValue, key?: string): NodeFire<TScope> {
return new NodeFire(this.$ref.startAt(value, key), this.$scope);
}

endAt(value: PrimitiveValue, key?: string): NodeFire {
endAt(value: PrimitiveValue, key?: string): NodeFire<TScope> {
return new NodeFire(this.$ref.endAt(value, key), this.$scope);
}

orderByKey(): NodeFire {
orderByKey(): NodeFire<TScope> {
return new NodeFire(this.$ref.orderByKey(), this.$scope);
}

orderByValue(): NodeFire {
orderByValue(): NodeFire<TScope> {
return new NodeFire(this.$ref.orderByValue(), this.$scope);
}
}
Expand All @@ -748,10 +752,10 @@ export default class NodeFire {
ref returns a NodeFire instance, val() normalizes the value, and child() takes an optional
refining scope.
*/
export class Snapshot {
export class Snapshot<TScope extends Scope> {
$snap: admin.database.DataSnapshot;
$nodeFire: NodeFire;
constructor(snap: admin.database.DataSnapshot, nodeFire: NodeFire) {
$nodeFire: NodeFire<TScope>;
constructor(snap: admin.database.DataSnapshot, nodeFire: NodeFire<TScope>) {
this.$snap = snap;
this.$nodeFire = nodeFire;
}
Expand All @@ -760,20 +764,20 @@ export class Snapshot {
return this.$snap.key;
}

get ref(): NodeFire {
get ref(): NodeFire<TScope> {
return new NodeFire(this.$snap.ref, this.$nodeFire.$scope);
}

val(): Value {
return getNormalValue(this.$snap);
}

child(path: string, scope: Scope): Snapshot {
child<TSource extends Scope>(path: string, scope: TSource): Snapshot<TScope & TSource> {
const childNodeFire = this.$nodeFire.scope(scope);
return new Snapshot(this.$snap.child(childNodeFire.interpolate(path)), childNodeFire);
}

forEach(callback: (snapshot: Snapshot) => any): any {
forEach(callback: (snapshot: Snapshot<TScope>) => any): any {
this.$snap.forEach(child => {
return callback(new Snapshot(child, this.$nodeFire));
});
Expand Down Expand Up @@ -805,7 +809,7 @@ type CapturableCallback =
// wrappers is equal to the number of on()s for that callback, and we can safely pop one with each
// call to off().
function captureCallback(
nodeFire: NodeFire,
nodeFire: NodeFire<Scope>,
eventType: admin.database.EventType,
callback: CapturableCallback,
): (a: admin.database.DataSnapshot, b?: string) => any {
Expand All @@ -821,7 +825,7 @@ function captureCallback(
}

function popCallback(
nodeFire: NodeFire, eventType: admin.database.EventType, callback: CapturableCallback
nodeFire: NodeFire<Scope>, eventType: admin.database.EventType, callback: CapturableCallback
): NodeFireCallback {
const key = eventType + '::' + nodeFire.toString();
return callback.$nodeFireCallbacks[key].pop();
Expand All @@ -846,7 +850,7 @@ function delegateSnapshot(method) {
};
}

function wrapReject(nodefire: NodeFire, method, value, reject?) {
function wrapReject(nodefire: NodeFire<Scope>, method, value, reject?) {
let hasValue = true;
if (!reject) {
reject = value;
Expand Down