Skip to content

Conversation

@shettyvarun268
Copy link

This PR adds the v1-compatible argument getters (context and message) directly to the v2 Pub/Sub CloudEvent class.
This allows developers to access v1-style arguments via property access, e.g., const { message, context } = event;, for a smoother migration of existing business logic to the v2 function signature.Implementation Details

The v1-compatible context getter is implemented as a lazily-computed property on the CloudEvent object. To correctly construct the v1 EventContext from the v2 event's properties, the following two internal helper functions are introduced:

  1. attachPubSubContext<T>(event: CloudEvent<MessagePublishedData<T>>, topic: string):
    Adds the v1-style context property to the v2 event object using Object.defineProperty.
    The getter computes the EventContext by mapping v2 properties (event.id, event.time) and by calling getResourceName.
    This ensures the context is only computed once upon first access.

  2. getResourceName(event: CloudEvent<MessagePublishedData<any>>, topic: string):
    Extracts the full resource name for the v1 EventContext resource object.
    It attempts to parse the project ID and topic name from event.source or falls back to environment variables and the function's topic name parameter if necessary.

This is the first of a series of PRs aimed at simplifying the migration from v1 to v2. By introducing this new
onMessagePublished function, we are providing a more robust and flexible way to handle Pub/Sub events in Cloud Functions for Firebase v2.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @shettyvarun268, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant enhancement to the v2 Pub/Sub CloudEvent handling by integrating v1-compatible argument accessors. The primary goal is to facilitate a seamless migration for developers transitioning their existing v1 Cloud Functions to the v2 signature. By providing direct property access to message and context in a v1-like manner, it reduces the friction associated with adapting business logic to the new event structure, making the upgrade path more straightforward.

Highlights

  • V1 Compatibility: Adds v1-compatible context and message getters directly to the v2 Pub/Sub CloudEvent<MessagePublishedData> class.
  • Migration Simplification: Enables direct property access (e.g., const { message, context } = event;) for smoother migration of existing v1 business logic to v2 function signatures.
  • Lazy Context Computation: Implements the v1-compatible context getter as a lazily-computed property on the CloudEvent object, ensuring it's only computed upon first access.
  • New Helper Functions: Introduces two internal helper functions: attachPubSubContext to add the v1-style context and getResourceName to extract the full resource name for the v1 EventContext, including robust fallback logic for project and topic names.
  • Strategic Initiative: This pull request is the first in a series aimed at simplifying the overall migration process from v1 to v2 Cloud Functions for Firebase.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@shettyvarun268
Copy link
Author

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a v1-compatibility layer for v2 Pub/Sub functions by adding a lazily-computed context property to the CloudEvent object. The implementation is well-structured, using Object.defineProperty for lazy evaluation and providing fallbacks for constructing the resource name. I've identified a bug in the resource name construction for a fallback case and a couple of areas for improvement in the tests to make them more robust and accurate. My feedback includes a high-severity fix for the resource name format and medium-severity suggestions to improve test coverage and correctness.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request successfully implements V1-compatible API for PubSub events in V2 Cloud Functions, which will greatly assist in migrating existing business logic. The changes introduce "context" and "message" getters to the CloudEvent<MessagePublishedData> class, along with robust helper functions attachPubSubContext and getResourceName to construct the V1 EventContext. The new test cases thoroughly cover the functionality, including handling existing contexts and fallback mechanisms for resource name extraction.

* @param event - The event to add the context to.
* @param topic - The topic the event is for.
*/
function attachPubSubContext<T>(event: CloudEvent<MessagePublishedData<T>>, topic: string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think we strictly need topic. all information should be derivable from event obj.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made the change and got rid of the topic.

*/
function getResourceName(event: CloudEvent<MessagePublishedData<any>>, topic: string) {
const match = event.source?.match(/projects\/([^/]+)\/topics\/([^/]+)/);
const project =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this code is overly defensive.

cloudevent is a standard format, and it's unlikely you won't be able to retrieve project/topic information from the event payload. defaulitng to "unknown" or "" is probably fine in the unlikely case we can't derive the info.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Removed the condition. Also removed the corresponding test case because it won't be needed.

messagePublishedData.message = new Message(messagePublishedData.message);
return wrapTraceContext(withInit(handler))(raw as CloudEvent<MessagePublishedData<T>>);
const event = raw as CloudEvent<MessagePublishedData<T>>;
attachPubSubContext(event, topic);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ptal look at v1 pubsub signature:

 (message: Message, context: EventContext) => PromiseLike<any> 

We also need a getter for message as well as context

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Made the needed changes. Added the message getter. This would allow us to access event.messag directly now.

);
});

//Test case to ensure Idempotency. makes things dont break if there is already context present
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment seems redudant

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted. Addressed this by removing the test case below.


let receivedEvent: CloudEvent<pubsub.MessagePublishedData<any>>;
const func = pubsub.onMessagePublished("topic", (e) => {
receivedEvent = e;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd prefer we try to use the context here directly:

Suggested change
receivedEvent = e;
{ message, context } = e;

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made the changes. Used deconstructedMessage and context instead

});

//Test case to ensure Idempotency. makes things dont break if there is already context present
it("should not modify a CloudEvent that already has a context", async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure why this exist? why would there be existing context?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. The real cases won't have an existing case. Removed this test case.

Comment on lines +96 to +100
/** V1- compatible context of this event.
*
* This getter is added at runtime for V1 compatibility.
* May be undefined it not set by a provider
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you like me to make the comments more informative and developer friendly. Considering it might be put into the reference docs directly from here?

@jhuleatt jhuleatt requested a review from egilmorez December 5, 2025 18:57

};

Object.defineProperty(event, "context", {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for my own learning, is defineProperty used here so that event.context isn't settable?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's correct. This is to ensure event.context a read-only property so as to not accidentally end up changing it inside the function.

* @packageDocumentation
*/

import type { EventContext } from "../v1/cloud-functions";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be worth redefining this as 1stGenEventContext or LegacyEventContext so that it's clear it is not really meant for people using 2nd gen?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does v1EventContext sound more clear and concise?

* @param topic - The topic the event is for.
*/
function attachPubSubContext<T>(event: CloudEvent<MessagePublishedData<T>>, topic: string) {
if ("context" in event && event.context) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when would context already be in the event? should this throw if that happens?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Changed the code to throw an error in this case. The context existing would be unlikely. Would you prefer I get rid of the check altogether?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants