Skip to content

Commit 92d7be3

Browse files
authored
Use conditional typing for defining the right set of options (#166)
* Use conditionally typing for defining the right set of options * Provide another overflow for the general wrap function
1 parent 4080850 commit 92d7be3

File tree

2 files changed

+40
-15
lines changed

2 files changed

+40
-15
lines changed

src/main.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@
2020
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121
// SOFTWARE.
2222

23-
import { CloudFunction as CloudFunctionV1 } from 'firebase-functions';
23+
import {
24+
CloudFunction as CloudFunctionV1,
25+
HttpsFunction,
26+
Runnable,
27+
} from 'firebase-functions';
2428

2529
import {
2630
CloudFunction as CloudFunctionV2,
@@ -31,6 +35,11 @@ import { wrapV1, WrappedFunction, WrappedScheduledFunction } from './v1';
3135

3236
import { wrapV2, WrappedV2Function } from './v2';
3337

38+
type HttpsFunctionOrCloudFunctionV1<T, U> = U extends HttpsFunction &
39+
Runnable<T>
40+
? HttpsFunction & Runnable<T>
41+
: CloudFunctionV1<T>;
42+
3443
// Re-exporting V1 (to reduce breakage)
3544
export {
3645
ContextOptions,
@@ -45,20 +54,24 @@ export {
4554
// V2 Exports
4655
export { WrappedV2Function } from './v2';
4756

57+
export function wrap<T>(
58+
cloudFunction: HttpsFunction & Runnable<T>
59+
): WrappedFunction<T, HttpsFunction & Runnable<T>>;
4860
export function wrap<T>(
4961
cloudFunction: CloudFunctionV1<T>
5062
): WrappedScheduledFunction | WrappedFunction<T>;
5163
export function wrap<T extends CloudEvent<unknown>>(
5264
cloudFunction: CloudFunctionV2<T>
5365
): WrappedV2Function<T>;
54-
5566
export function wrap<T, V extends CloudEvent<unknown>>(
5667
cloudFunction: CloudFunctionV1<T> | CloudFunctionV2<V>
5768
): WrappedScheduledFunction | WrappedFunction<T> | WrappedV2Function<V> {
5869
if (isV2CloudFunction<V>(cloudFunction)) {
5970
return wrapV2<V>(cloudFunction as CloudFunctionV2<V>);
6071
}
61-
return wrapV1<T>(cloudFunction as CloudFunctionV1<T>);
72+
return wrapV1<T>(
73+
cloudFunction as HttpsFunctionOrCloudFunctionV1<T, typeof cloudFunction>
74+
);
6275
}
6376

6477
/**

src/v1.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import {
3030
config,
3131
database,
3232
firestore,
33+
HttpsFunction,
34+
Runnable,
3335
} from 'firebase-functions';
3436

3537
/** Fields of the event context that can be overridden/customized. */
@@ -88,14 +90,16 @@ export type CallableContextOptions = {
8890
};
8991

9092
/* Fields for both Event and Callable contexts, checked at runtime */
91-
export type ContextOptions = EventContextOptions | CallableContextOptions;
93+
export type ContextOptions<T = void> = T extends HttpsFunction & Runnable<T>
94+
? CallableContextOptions
95+
: EventContextOptions;
9296

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

101105
/** A scheduled function that can be called with optional override values for the event context.
@@ -106,9 +110,12 @@ export type WrappedScheduledFunction = (
106110
) => any | Promise<any>;
107111

108112
/** Takes a cloud function to be tested, and returns a WrappedFunction which can be called in test code. */
113+
export function wrapV1<T>(
114+
cloudFunction: HttpsFunction & Runnable<T>
115+
): WrappedFunction<T, HttpsFunction & Runnable<T>>;
109116
export function wrapV1<T>(
110117
cloudFunction: CloudFunction<T>
111-
): WrappedScheduledFunction | WrappedFunction<T> {
118+
): WrappedScheduledFunction | WrappedFunction<T, CloudFunction<T>> {
112119
if (!has(cloudFunction, '__trigger')) {
113120
throw new Error(
114121
'Wrap can only be called on functions written with the firebase-functions SDK.'
@@ -150,26 +157,26 @@ export function wrapV1<T>(
150157
const isCallableFunction =
151158
get(cloudFunction, '__trigger.labels.deployment-callable') === 'true';
152159

153-
let wrapped: WrappedFunction<T> = (data: T, options: ContextOptions) => {
160+
let wrapped: WrappedFunction<T, typeof cloudFunction> = (data, options) => {
154161
// Although in Typescript we require `options` some of our JS samples do not pass it.
155-
options = options || {};
162+
const _options = { ...options };
156163
let context;
157164

158165
if (isCallableFunction) {
159166
_checkOptionValidity(
160167
['app', 'auth', 'instanceIdToken', 'rawRequest'],
161-
options
168+
_options
162169
);
163-
let callableContextOptions = options as CallableContextOptions;
170+
let callableContextOptions = _options as CallableContextOptions;
164171
context = {
165172
...callableContextOptions,
166173
};
167174
} else {
168175
_checkOptionValidity(
169176
['eventId', 'timestamp', 'params', 'auth', 'authType', 'resource'],
170-
options
177+
_options
171178
);
172-
const defaultContext = _makeDefaultContext(cloudFunction, options, data);
179+
const defaultContext = _makeDefaultContext(cloudFunction, _options, data);
173180

174181
if (
175182
has(defaultContext, 'eventType') &&
@@ -179,7 +186,7 @@ export function wrapV1<T>(
179186
defaultContext.authType = 'UNAUTHENTICATED';
180187
defaultContext.auth = null;
181188
}
182-
context = merge({}, defaultContext, options);
189+
context = merge({}, defaultContext, _options);
183190
}
184191

185192
return cloudFunction.run(data, context);
@@ -226,9 +233,14 @@ function _checkOptionValidity(
226233
});
227234
}
228235

236+
function _makeDefaultContext<T>(
237+
cloudFunction: HttpsFunction & Runnable<T>,
238+
options: CallableContextOptions,
239+
triggerData?: T
240+
);
229241
function _makeDefaultContext<T>(
230242
cloudFunction: CloudFunction<T>,
231-
options: ContextOptions,
243+
options: EventContextOptions,
232244
triggerData?: T
233245
): EventContext {
234246
let eventContextOptions = options as EventContextOptions;

0 commit comments

Comments
 (0)