Skip to content

Commit c8fd6d9

Browse files
authored
Merge branch 'master' into inlined.ail-fix
2 parents d8bf758 + d416aca commit c8fd6d9

7 files changed

Lines changed: 166 additions & 8 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "firebase-functions",
3-
"version": "7.2.2",
3+
"version": "7.2.4",
44
"description": "Firebase SDK for Cloud Functions",
55
"keywords": [
66
"firebase",

scripts/publish/npmrc.enc

0 Bytes
Binary file not shown.

spec/v2/providers/https.spec.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { FULL_ENDPOINT, MINIMAL_V2_ENDPOINT, FULL_OPTIONS, FULL_TRIGGER } from "
3232
import { onInit } from "../../../src/v2/core";
3333
import { Handler } from "express";
3434
import { genkit } from "genkit";
35-
import { clearParams, defineList, Expression } from "../../../src/params";
35+
import { clearParams, defineBoolean, defineList, Expression } from "../../../src/params";
3636

3737
function request(args: {
3838
data?: any;
@@ -507,6 +507,43 @@ describe("onCall", () => {
507507
});
508508
});
509509

510+
it("should allow boolean params for enforceAppCheck", async () => {
511+
const enforceAppCheck = defineBoolean("ENFORCE_APP_CHECK");
512+
try {
513+
process.env.ENFORCE_APP_CHECK = "true";
514+
const func = https.onCall({ enforceAppCheck }, () => 42);
515+
516+
const req = request({ headers: { origin: "example.com" } }); // No app check token
517+
const resp = await runHandler(func, req);
518+
expect(resp.status).to.equal(401);
519+
} finally {
520+
delete process.env.ENFORCE_APP_CHECK;
521+
clearParams();
522+
}
523+
});
524+
525+
it("should allow boolean params for consumeAppCheckToken", async () => {
526+
const consumeAppCheckToken = defineBoolean("CONSUME_APP_CHECK_TOKEN");
527+
sinon.stub(debug, "isDebugFeatureEnabled").withArgs("skipTokenVerification").returns(true);
528+
try {
529+
process.env.CONSUME_APP_CHECK_TOKEN = "true";
530+
const func = https.onCall({ consumeAppCheckToken }, (request) => {
531+
return { alreadyConsumed: request.app?.alreadyConsumed };
532+
});
533+
534+
const req = request({ headers: { "X-Firebase-AppCheck": "valid_token_ignored_on_skip" } });
535+
const resp = await runHandler(func, req);
536+
537+
expect(resp.status).to.equal(200);
538+
const result = JSON.parse(resp.body).result;
539+
expect(result.alreadyConsumed).to.satisfy((v) => v === false || v === null);
540+
} finally {
541+
delete process.env.CONSUME_APP_CHECK_TOKEN;
542+
clearParams();
543+
sinon.restore();
544+
}
545+
});
546+
510547
it("overrides CORS headers if debug feature is enabled", async () => {
511548
sinon.stub(debug, "isDebugFeatureEnabled").withArgs("enableCors").returns(true);
512549

spec/v2/providers/pubsub.spec.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,114 @@ describe("onMessagePublished", () => {
220220
expect(result).to.deep.equal({ test: "data" });
221221
});
222222

223+
describe("event.data.message wrapping (POJO -> Message instance)", () => {
224+
const rawMessagePOJO = {
225+
messageId: "pojo-msg-id",
226+
data: Buffer.from(JSON.stringify({ regression: "test" })).toString("base64"),
227+
attributes: { env: "test" },
228+
orderingKey: "key1",
229+
publishTime: new Date().toISOString(),
230+
};
231+
232+
function makeRawEvent(
233+
message: any,
234+
subscription = "projects/aProject/subscriptions/aSub"
235+
): CloudEvent<any> {
236+
return {
237+
specversion: "1.0",
238+
source: "//pubsub.googleapis.com/projects/aProject/topics/topic",
239+
id: "raw-event-id",
240+
type: EVENT_TRIGGER.eventType,
241+
time: rawMessagePOJO.publishTime,
242+
data: { message, subscription },
243+
};
244+
}
245+
246+
it("should convert a raw POJO message into a Message instance on event.data.message", async () => {
247+
let capturedMessage: any;
248+
const func = pubsub.onMessagePublished("topic", (event) => {
249+
capturedMessage = event.data.message;
250+
});
251+
252+
// Pass a raw POJO, NOT a Message instance — this is what the runtime delivers
253+
await func(makeRawEvent({ ...rawMessagePOJO }));
254+
255+
expect(capturedMessage).to.be.an.instanceOf(pubsub.Message);
256+
});
257+
258+
it("should provide a working .json getter on event.data.message when input is a raw POJO", async () => {
259+
let json: unknown;
260+
const func = pubsub.onMessagePublished("topic", (event) => {
261+
json = event.data.message.json;
262+
});
263+
264+
await func(makeRawEvent({ ...rawMessagePOJO }));
265+
266+
expect(json).to.deep.equal({ regression: "test" });
267+
});
268+
269+
it("should preserve all Message fields when wrapping a raw POJO", async () => {
270+
let msg: any;
271+
const func = pubsub.onMessagePublished("topic", (event) => {
272+
msg = event.data.message;
273+
});
274+
275+
await func(makeRawEvent({ ...rawMessagePOJO }));
276+
277+
expect(msg.messageId).to.equal("pojo-msg-id");
278+
expect(msg.data).to.equal(rawMessagePOJO.data);
279+
expect(msg.attributes).to.deep.equal({ env: "test" });
280+
expect(msg.orderingKey).to.equal("key1");
281+
expect(msg.publishTime).to.equal(rawMessagePOJO.publishTime);
282+
});
283+
284+
it("should not re-wrap if event.data.message is already a Message instance", async () => {
285+
const original = new pubsub.Message(rawMessagePOJO);
286+
let capturedMessage: any;
287+
const func = pubsub.onMessagePublished("topic", (event) => {
288+
capturedMessage = event.data.message;
289+
});
290+
291+
await func(makeRawEvent(original));
292+
293+
expect(capturedMessage).to.equal(original); // same reference
294+
expect(capturedMessage.json).to.deep.equal({ regression: "test" });
295+
});
296+
297+
it("should throw on a malformed event without a message property", async () => {
298+
const func = pubsub.onMessagePublished("topic", () => undefined);
299+
const badEvent: CloudEvent<any> = {
300+
specversion: "1.0",
301+
source: "//pubsub.googleapis.com/projects/aProject/topics/topic",
302+
id: "bad-event",
303+
type: EVENT_TRIGGER.eventType,
304+
time: new Date().toISOString(),
305+
data: { subscription: "sub" }, // no message!
306+
};
307+
308+
try {
309+
await func(badEvent);
310+
expect.fail("should have thrown");
311+
} catch (err: any) {
312+
expect(err.message).to.match(/missing 'message' property/i);
313+
}
314+
});
315+
316+
it("should make event.data.message.json and event.message.json return the same value", async () => {
317+
let v2Json: unknown;
318+
let v1Json: unknown;
319+
const func = pubsub.onMessagePublished("topic", (event) => {
320+
v2Json = event.data.message.json;
321+
v1Json = (event as any).message.json;
322+
});
323+
324+
await func(makeRawEvent({ ...rawMessagePOJO }));
325+
326+
expect(v2Json).to.deep.equal({ regression: "test" });
327+
expect(v1Json).to.deep.equal({ regression: "test" });
328+
});
329+
});
330+
223331
describe("v1-compatible getters", () => {
224332
let capturedEvent: any;
225333
const messageData = {

src/v2/providers/https.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ export interface CallableOptions<T = any> extends HttpsOptions {
183183
* (Unauthorized) error.
184184
* When false, requests with invalid tokens set event.app to undefiend.
185185
*/
186-
enforceAppCheck?: boolean;
186+
enforceAppCheck?: boolean | Expression<boolean>;
187187

188188
/**
189189
* Determines whether Firebase App Check token is consumed on request. Defaults to false.
@@ -207,7 +207,7 @@ export interface CallableOptions<T = any> extends HttpsOptions {
207207
* the request.app.alreadyConsumed property will be set to true and pass the execution to the handler code for making
208208
* further decisions, such as requiring additional security checks or rejecting the request.
209209
*/
210-
consumeAppCheckToken?: boolean;
210+
consumeAppCheckToken?: boolean | Expression<boolean>;
211211

212212
/**
213213
* Time in seconds between sending heartbeat messages to keep the connection
@@ -455,11 +455,22 @@ export function onCall<T = any, Return = any | Promise<any>, Stream = unknown>(
455455

456456
// fix the length of handler to make the call to handler consistent
457457
const fixedLen = (req: CallableRequest<T>, resp?: CallableResponse<Stream>) => handler(req, resp);
458+
459+
let enforceAppCheck = opts.enforceAppCheck ?? options.getGlobalOptions().enforceAppCheck;
460+
if (enforceAppCheck instanceof Expression) {
461+
enforceAppCheck = enforceAppCheck.value();
462+
}
463+
464+
let consumeAppCheckToken = opts.consumeAppCheckToken;
465+
if (consumeAppCheckToken instanceof Expression) {
466+
consumeAppCheckToken = consumeAppCheckToken.value();
467+
}
468+
458469
let func: any = onCallHandler(
459470
{
460471
cors: { origin, methods: "POST" },
461-
enforceAppCheck: opts.enforceAppCheck ?? options.getGlobalOptions().enforceAppCheck,
462-
consumeAppCheckToken: opts.consumeAppCheckToken,
472+
enforceAppCheck,
473+
consumeAppCheckToken,
463474
heartbeatSeconds: opts.heartbeatSeconds,
464475
authPolicy: opts.authPolicy,
465476
},

src/v2/providers/pubsub.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,8 @@ export function onMessagePublished<T = any>(
346346
pubsubData.message instanceof Message
347347
? pubsubData.message
348348
: new Message<T>(pubsubData.message);
349+
350+
(pubsubData as any).message = v2Message;
349351
} else {
350352
throw new Error("Malformed Pub/Sub event: missing 'message' property.");
351353
}

0 commit comments

Comments
 (0)