Skip to content

refactor: code improvements #264

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
4 changes: 2 additions & 2 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
node-version: ${{ matrix.node-version }}

- name: Cache npm
uses: actions/cache@v1
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
Expand All @@ -45,7 +45,7 @@ jobs:
node-version: ${{ matrix.node-version }}

- name: Cache npm
uses: actions/cache@v1
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
Expand Down
2 changes: 1 addition & 1 deletion spec/providers/firestore.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect } from 'chai';
import * as firebase from 'firebase-admin';
import { FeaturesList } from '../../src/features';
import { FeaturesList } from '../../src/types/commonTypes';
import fft = require('../../src/index');

describe('providers/firestore', () => {
Expand Down
1 change: 0 additions & 1 deletion spec/v2.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ import {
import { defineString } from 'firebase-functions/params';
import { makeDataSnapshot } from '../src/providers/database';
import { makeDocumentSnapshot } from '../src/providers/firestore';
import { inspect } from 'util';

describe('v2', () => {
describe('#wrapV2', () => {
Expand Down
5 changes: 3 additions & 2 deletions src/cloudevent/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import {
DocumentSnapshot,
QueryDocumentSnapshot,
} from 'firebase-admin/firestore';
import { LIST_OF_MOCK_CLOUD_EVENT_PARTIALS } from './mocks/partials';
import { DeepPartial } from './types';
import { Change } from 'firebase-functions/v1';
import merge from 'ts-deepmerge';

import { LIST_OF_MOCK_CLOUD_EVENT_PARTIALS } from './mocks/partials';
import { DeepPartial } from './types';

/**
* @return {CloudEvent} Generated Mock CloudEvent
*/
Expand Down
18 changes: 1 addition & 17 deletions src/features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,7 @@ import * as database from './providers/database';
import * as firestore from './providers/firestore';
import * as pubsub from './providers/pubsub';
import * as storage from './providers/storage';
import { FirebaseFunctionsTest } from './lifecycle';

export interface LazyFeatures {
mockConfig: typeof mockConfig;
wrap: typeof wrap;
makeChange: typeof makeChange;
analytics: typeof analytics;
auth: typeof auth;
database: typeof database;
firestore: typeof firestore;
pubsub: typeof pubsub;
storage: typeof storage;
}
import { LazyFeatures } from './types/commonTypes';

export const features: LazyFeatures = {
mockConfig,
Expand All @@ -30,7 +18,3 @@ export const features: LazyFeatures = {
pubsub,
storage,
};

export interface FeaturesList extends LazyFeatures {
cleanup: InstanceType<typeof FirebaseFunctionsTest>['cleanup'];
}
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { AppOptions } from 'firebase-admin';
import { merge } from 'lodash';

import { FirebaseFunctionsTest } from './lifecycle';
import { FeaturesList } from './features';
import { FeaturesList } from './types/commonTypes';

export = (
firebaseConfig?: AppOptions,
Expand Down
52 changes: 22 additions & 30 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,35 @@ import {
HttpsFunction,
Runnable,
} from 'firebase-functions/v1';

import {
CloudFunction as CloudFunctionV2,
CloudEvent,
} from 'firebase-functions/v2';

import {
CallableFunction,
HttpsFunction as HttpsFunctionV2,
} from 'firebase-functions/v2/https';

import { wrapV1, WrappedFunction, WrappedScheduledFunction } from './v1';

import { wrapV2, WrappedV2Function, WrappedV2CallableFunction } from './v2';
import { wrapV1 } from './v1';
import { wrapV2 } from './v2';
import { WrappedFunction, WrappedScheduledFunction } from './types/v1Types';
import { WrappedV2Function, WrappedV2CallableFunction } from './types/v2Types';
import { HttpsFunctionOrCloudFunctionV1 } from './types/v1Types';

type HttpsFunctionOrCloudFunctionV1<T, U> = U extends HttpsFunction &
Runnable<T>
? HttpsFunction & Runnable<T>
: CloudFunctionV1<T>;
/**
* The key differences between V1 and V2 CloudFunctions are:
* <ul>
* <li> V1 CloudFunction is sometimes a binary function
* <li> V2 CloudFunction is always a unary function
* <li> V1 CloudFunction.run is always a binary function
* <li> V2 CloudFunction.run is always a unary function
* @return True iff the CloudFunction is a V2 function.
*/
function isV2CloudFunction<T extends CloudEvent<unknown>>(
cloudFunction: any
): cloudFunction is CloudFunctionV2<T> {
return cloudFunction.length === 1 && cloudFunction?.run?.length === 1;
}

// Re-exporting V1 (to reduce breakage)
export {
Expand All @@ -52,13 +62,10 @@ export {
WrappedFunction,
WrappedScheduledFunction,
CallableContextOptions,
makeChange,
mockConfig,
} from './v1';

} from './types/v1Types';
export { makeChange, mockConfig } from './v1';
// V2 Exports
export { WrappedV2Function } from './v2';

export { WrappedV2Function } from './types/v2Types';
export function wrap<T>(
cloudFunction: HttpsFunction & Runnable<T>
): WrappedFunction<T, HttpsFunction & Runnable<T>>;
Expand All @@ -85,18 +92,3 @@ export function wrap<T, V extends CloudEvent<unknown>>(
cloudFunction as HttpsFunctionOrCloudFunctionV1<T, typeof cloudFunction>
);
}

/**
* The key differences between V1 and V2 CloudFunctions are:
* <ul>
* <li> V1 CloudFunction is sometimes a binary function
* <li> V2 CloudFunction is always a unary function
* <li> V1 CloudFunction.run is always a binary function
* <li> V2 CloudFunction.run is always a unary function
* @return True iff the CloudFunction is a V2 function.
*/
function isV2CloudFunction<T extends CloudEvent<unknown>>(
cloudFunction: any
): cloudFunction is CloudFunctionV2<T> {
return cloudFunction.length === 1 && cloudFunction?.run?.length === 1;
}
8 changes: 1 addition & 7 deletions src/providers/firestore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,10 @@
import { Change } from 'firebase-functions/v1';
import { firestore, app } from 'firebase-admin';
import { has, get, isEmpty, isPlainObject, mapValues } from 'lodash';
import { inspect } from 'util';
import * as http from 'http';

import { testApp } from '../app';

import * as http from 'http';
import {
DocumentSnapshot,
QueryDocumentSnapshot,
} from 'firebase-admin/firestore';

function dateToTimestampProto(
timeString?: string
): { seconds: number; nanos: number } | undefined {
Expand Down
24 changes: 24 additions & 0 deletions src/types/commonTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { makeChange, wrap, mockConfig } from '../main';
import * as analytics from '../providers/analytics';
import * as auth from '../providers/auth';
import * as database from '../providers/database';
import * as firestore from '../providers/firestore';
import * as pubsub from '../providers/pubsub';
import * as storage from '../providers/storage';
import { FirebaseFunctionsTest } from '../lifecycle';

export interface LazyFeatures {
mockConfig: typeof mockConfig;
wrap: typeof wrap;
makeChange: typeof makeChange;
analytics: typeof analytics;
auth: typeof auth;
database: typeof database;
firestore: typeof firestore;
pubsub: typeof pubsub;
storage: typeof storage;
}

export interface FeaturesList extends LazyFeatures {
cleanup: InstanceType<typeof FirebaseFunctionsTest>['cleanup'];
}
86 changes: 86 additions & 0 deletions src/types/v1Types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
CloudFunction as CloudFunctionV1,
https,
HttpsFunction,
Runnable,
} from 'firebase-functions/v1';

/** Fields of the event context that can be overridden/customized. */
export type EventContextOptions = {
/** ID of the event. If omitted, a random ID will be generated. */
eventId?: string;
/** ISO time string of when the event occurred. If omitted, the current time is used. */
timestamp?: string;
/** The values for the wildcards in the reference path that a database or Firestore function is listening to.
* If omitted, random values will be generated.
*/
params?: { [option: string]: any };
/** (Only for database functions and https.onCall.) Firebase auth variable representing the user that triggered
* the function. Defaults to null.
*/
auth?: any;
/** (Only for database and https.onCall functions.) The authentication state of the user that triggered the function.
* Default is 'UNAUTHENTICATED'.
*/
authType?: 'ADMIN' | 'USER' | 'UNAUTHENTICATED';

/** Resource is a standard format for defining a resource (google.rpc.context.AttributeContext.Resource).
* In Cloud Functions, it is the resource that triggered the function - such as a storage bucket.
*/
resource?: {
service: string;
name: string;
type?: string;
labels?: {
[tag: string]: string;
};
};
};

/** Fields of the callable context that can be overridden/customized. */
export type CallableContextOptions = {
/**
* The result of decoding and verifying a Firebase AppCheck token.
*/
app?: any;

/**
* The result of decoding and verifying a Firebase Auth ID token.
*/
auth?: any;

/**
* An unverified token for a Firebase Instance ID.
*/
instanceIdToken?: string;

/**
* The raw HTTP request object.
*/
rawRequest?: https.Request;
};

/* Fields for both Event and Callable contexts, checked at runtime */
export type ContextOptions<T = void> = T extends HttpsFunction & Runnable<T>
? CallableContextOptions
: EventContextOptions;

/** A function that can be called with test data and optional override values for the event context.
* It will subsequently invoke the cloud function it wraps with the provided test data and a generated event context.
*/
export type WrappedFunction<T, U = void> = (
data: T,
options?: ContextOptions<U>
) => any | Promise<any>;

/** A scheduled function that can be called with optional override values for the event context.
* It will subsequently invoke the cloud function it wraps with a generated event context.
*/
export type WrappedScheduledFunction = (
options?: ContextOptions
) => any | Promise<any>;

export type HttpsFunctionOrCloudFunctionV1<T, U> = U extends HttpsFunction &
Runnable<T>
? HttpsFunction & Runnable<T>
: CloudFunctionV1<T>;
14 changes: 14 additions & 0 deletions src/types/v2Types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { CallableRequest } from 'firebase-functions/v2/https';
import { DeepPartial } from '../cloudevent/types';
import { CloudEvent } from 'firebase-functions/v2';

/** A function that can be called with test data and optional override values for {@link CloudEvent}
* It will subsequently invoke the cloud function it wraps with the provided {@link CloudEvent}
*/
export type WrappedV2Function<T extends CloudEvent<unknown>> = (
cloudEventPartial?: DeepPartial<T | object>
) => any | Promise<any>;

export type WrappedV2CallableFunction<T> = (
data: CallableRequest
) => T | Promise<T>;
Loading
Loading