Skip to content
Merged
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
58 changes: 58 additions & 0 deletions spec/main.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,64 @@ describe('main', () => {
});
});

describe('v2 callable functions', () => {
it('should route to v2 path when outer and run have length 0', () => {
Comment thread
IzaakGough marked this conversation as resolved.
Comment thread
IzaakGough marked this conversation as resolved.
const cloudFunction = (..._args: unknown[]) => {};
set(cloudFunction, 'run', (...args: unknown[]) => {
const [req] = args;
return { auth: (req as any).auth };
});
set(cloudFunction, '__endpoint', {
platform: 'gcfv2',
callableTrigger: {},
});
const wrapped = wrap(cloudFunction as functions.CloudFunction<any>);
const result = wrapped({
data: { ping: true },
auth: { uid: 'test-user' },
});
expect(result.auth).to.deep.equal({ uid: 'test-user' });
});
});

describe('v2 onRequest functions', () => {
it('should throw the onCall-only error', () => {
const cloudFunction = (_req, _res) => {};
set(cloudFunction, '__endpoint', {
platform: 'gcfv2',
httpsTrigger: {},
});
expect(() => wrap(cloudFunction as any)).to.throw(
'Wrap function is only available for `onCall` HTTP functions, not `onRequest`.'
);
});
});

describe('v1 callable functions with endpoint metadata', () => {
it('should route v1 callables with callableTrigger metadata to wrapV1', () => {
const cloudFunction = (input) => input;
set(cloudFunction, 'run', (data, context) => {
return { data, context };
});
set(cloudFunction, '__endpoint', {
platform: 'gcfv1',
callableTrigger: {},
});
const wrapped: any = wrap(cloudFunction as any);
const result = wrapped('data', {
auth: { uid: 'abc' },
app: { appId: 'efg' },
instanceIdToken: '123',
rawRequest: { body: 'hello' },
});
expect(result.data).to.equal('data');
expect(result.context.auth).to.deep.equal({ uid: 'abc' });
expect(result.context.app).to.deep.equal({ appId: 'efg' });
expect(result.context.instanceIdToken).to.equal('123');
expect(result.context.rawRequest).to.deep.equal({ body: 'hello' });
});
});

describe('callable functions', () => {
let wrappedCF;

Expand Down
12 changes: 11 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ import {

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

import { wrapV2, WrappedV2Function, WrappedV2CallableFunction } from './v2';
import {
wrapV2,
WrappedV2Function,
WrappedV2CallableFunction,
isCallableV2Function,
} from './v2';

type HttpsFunctionOrCloudFunctionV1<T, U> = U extends HttpsFunction &
Runnable<T>
Expand Down Expand Up @@ -80,6 +85,11 @@ export function wrap<T, V extends CloudEvent<unknown>>(
| WrappedFunction<T>
| WrappedV2Function<V>
| WrappedV2CallableFunction<T> {
// v2 callables are checked by trigger rather than arity, since
// firebase-functions@7 wraps them in rest-param functions of length 0.
if (isCallableV2Function(cloudFunction)) {
Comment thread
IzaakGough marked this conversation as resolved.
Comment thread
IzaakGough marked this conversation as resolved.
return wrapV2(cloudFunction);
}
if (isV2CloudFunction<V>(cloudFunction)) {
Comment thread
IzaakGough marked this conversation as resolved.
return wrapV2<V>(cloudFunction as CloudFunctionV2<V>);
}
Expand Down
10 changes: 6 additions & 4 deletions src/v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@ export type WrappedV2CallableFunction<T> = (
data: CallableRequest
) => T | Promise<T>;

function isCallableV2Function<T extends CloudEvent<unknown>>(
cf: CloudFunction<T> | CallableFunction<any, any>
): cf is CallableFunction<any, any> {
return !!cf?.__endpoint?.callableTrigger;
export function isCallableV2Function(cf: {
__endpoint?: { platform?: string; callableTrigger?: unknown };
}): cf is CallableFunction<any, any> {
return (
!!cf?.__endpoint?.callableTrigger && cf.__endpoint.platform === 'gcfv2'
);
}

function assertIsCloudFunction<T extends CloudEvent<unknown>>(
Expand Down
Loading