Repository: firebase/firebase-tools (ts)

Issue:
Cannot create a function that depends on eventarc event filter"firebase.extensions.storage-resize-images.v1.complete"
<!-- DO NOT DELETE
validate_template=true
template_path=.github/ISSUE_TEMPLATE/bug_report.md
-->

<!--
Thank you for contributing to the Firebase community!

Think you found a bug?
=======================
Yeah, we're definitely not perfect! Please use this template and include a minimal repro when opening the issue. If you know how to solve the issue, please create a Pull Request, and we'd be happy to review it!

Have a feature request?
========================
Great, we love hearing how we can improve our products! However, do not use this template to submit a feature request. Please submit your feature requests to: https://firebase.google.com/support/contact/bugs-features/

Have a usage question?
=======================
We get lots of those and we love helping you, but GitHub is not the best place for them and they will be closed. Please take a look at the guide first: https://firebase.google.com/docs/cli/

If the official documentation doesn't help, try asking through our official support channel: https://firebase.google.com/support/

Additional locations to check for solutions or assistance from the community:
- Stack Overflow: https://stackoverflow.com/
- Firebase Slack Community: https://firebase.community/

*Please avoid duplicate posting across multiple channels!*
-->

### [REQUIRED] Environment info

<!-- What version of the Firebase CLI (`firebase-tools`) are you using? Note that your issue may already be fixed in the latest versions. The latest version can be found at https://github.com/firebase/firebase-tools/releases -->

11.2.2

<!-- Output of `firebase --version` -->

**firebase-tools:**

<!-- e.g. macOS, Windows, Ubuntu -->

macOS / Debian 11

**Platform:**

### [REQUIRED] Test case

<!-- Provide a minimal, complete, and verifiable example (http://stackoverflow.com/help/mcve) -->

1. Deploy the [resize images extension](https://firebase.google.com/products/extensions/firebase-storage-resize-images)
2. Deploy the [sample eventarc trigger](https://firebase.google.com/docs/functions/beta/custom-events#handle_a_custom_event)
```typescript
import {onCustomEventPublished} from "firebase-functions/v2/eventarc";
import {logger} from "firebase-functions";
import {initializeApp} from "firebase-admin/app";
import {getFirestore} from "firebase-admin/firestore";


// Start writing Firebase Functions
// https://firebase.google.com/docs/functions/typescript

initializeApp();

export const onimageresized = onCustomEventPublished(
    "firebase.extensions.storage-resize-images.v1.complete",
    (event: any) => {
      logger.info("Received image resize completed event", event);
      // For example, write resized image details into Firestore.
      return getFirestore()
          .collection("images")
          .doc(event.subject.replace("/", "_")) // original file path
          .set(event.data); // resized images paths and sizes
    });
```
3. When running npm run deploy, we get an error:
```
⚠  functions: HTTP Error: 400, Cannot create trigger projects/first-week-nohe-two/locations/us-central1/triggers/onimageresized-734752: The request was invalid: The request was invalid: invalid value for attribute 'type' in trigger.event_filters: "firebase.extensions.storage-resize-images.v1.complete"
⚠  functions: failed to create function projects/first-week-nohe-two/locations/us-central1/functions/onimageresized
Failed to create function projects/first-week-nohe-two/locations/us-central1/functions/onimageresized
```

### [REQUIRED] Steps to reproduce

See above

### [REQUIRED] Expected behavior

No error and it deploys fine

### [REQUIRED] Actual behavior

```
⚠  functions: HTTP Error: 400, Cannot create trigger projects/first-week-nohe-two/locations/us-central1/triggers/onimageresized-734752: The request was invalid: The request was invalid: invalid value for attribute 'type' in trigger.event_filters: "firebase.extensions.storage-resize-images.v1.complete"
⚠  functions: failed to create function projects/first-week-nohe-two/locations/us-central1/functions/onimageresized
Failed to create function projects/first-week-nohe-two/locations/us-central1/functions/onimageresized
```


Pull request:
Add optional fields into build and copy to backend
Fixes #4744

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index da1403811..bb7cf2f90 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,4 @@
 - Fix bug where functions:config get command failed on empty variable. (#4707)
 - Fix invoker type mismatch for https functions.
 - Add multi-tenancy and multi-factor authentication features in Emulator UI
+- Fix bug where not all the event trigger attributes are copied from build to backend (#4746).
diff --git a/src/deploy/functions/build.ts b/src/deploy/functions/build.ts
index b2b87c119..b7ba4167f 100644
--- a/src/deploy/functions/build.ts
+++ b/src/deploy/functions/build.ts
@@ -85,6 +85,7 @@ export interface BlockingTrigger {
 export interface EventTrigger {
   eventType: string;
   eventFilters: Record<string, Expression<string>>;
+  eventFilterPathPatterns?: Record<string, Expression<string>>;
 
   // whether failed function executions should retry the event execution.
   // Retries are indefinite, so developers should be sure to add some end condition (e.g. event
@@ -355,12 +356,22 @@ function discoverTrigger(
       eventFilters: bkEventFilters,
       retry: resolveBoolean(endpoint.eventTrigger.retry || false),
     };
+    if (endpoint.eventTrigger.eventFilterPathPatterns) {
+      const bkEventFiltersPathPatterns: Record<string, string> = {};
+      for (const [key, value] of Object.entries(endpoint.eventTrigger.eventFilterPathPatterns)) {
+        bkEventFiltersPathPatterns[key] = params.resolveString(value, paramValues);
+      }
+      bkEvent.eventFilterPathPatterns = bkEventFiltersPathPatterns;
+    }
     if (endpoint.eventTrigger.serviceAccount) {
       bkEvent.serviceAccountEmail = endpoint.eventTrigger.serviceAccount;
     }
     if (endpoint.eventTrigger.region) {
       bkEvent.region = resolveString(endpoint.eventTrigger.region);
     }
+    if (endpoint.eventTrigger.channel) {
+      bkEvent.channel = endpoint.eventTrigger.channel;
+    }
     trigger = { eventTrigger: bkEvent };
   } else if ("scheduleTrigger" in endpoint) {
     const bkSchedule: backend.ScheduleTrigger = {


Test patch:
diff --git a/src/test/deploy/functions/runtimes/discovery/v1alpha1.spec.ts b/src/test/deploy/functions/runtimes/discovery/v1alpha1.spec.ts
index 587151cd5..9a6a05acb 100644
--- a/src/test/deploy/functions/runtimes/discovery/v1alpha1.spec.ts
+++ b/src/test/deploy/functions/runtimes/discovery/v1alpha1.spec.ts
@@ -146,6 +146,48 @@ describe("buildFromV1Alpha", () => {
       expect(resolveBackend(parsed)).to.eventually.deep.equal(expectedBackend);
     });
 
+    it("copies event triggers with optional values", () => {
+      const eventTrigger: backend.EventTrigger = {
+        eventType: "some.event.type",
+        eventFilters: { resource: "my-resource" },
+        eventFilterPathPatterns: { instance: "my-instance" },
+        region: "us-central1",
+        serviceAccountEmail: "sa@",
+        retry: true,
+        channel: "projects/project/locations/region/channels/my-channel",
+      };
+      const newFormatTrigger: build.EventTrigger = {
+        eventType: "some.event.type",
+        eventFilters: { resource: "my-resource" },
+        eventFilterPathPatterns: { instance: "my-instance" },
+        region: "us-central1",
+        serviceAccount: "sa@",
+        retry: true,
+        channel: "projects/project/locations/region/channels/my-channel",
+      };
+      const yaml: v1alpha1.Manifest = {
+        specVersion: "v1alpha1",
+        endpoints: {
+          id: {
+            ...MIN_ENDPOINT,
+            eventTrigger,
+          },
+        },
+      };
+
+      const parsed = v1alpha1.buildFromV1Alpha1(yaml, PROJECT, REGION, RUNTIME);
+      const expected: build.Build = build.of({
+        id: { ...DEFAULTED_ENDPOINT, eventTrigger: newFormatTrigger },
+      });
+      expect(parsed).to.deep.equal(expected);
+
+      const expectedBackend = backend.of({
+        ...DEFAULTED_BACKEND_ENDPOINT,
+        eventTrigger,
+      });
+      expect(resolveBackend(parsed)).to.eventually.deep.equal(expectedBackend);
+    });
+
     it("copies event triggers with full resource path", () => {
       const eventTrigger: backend.EventTrigger = {
         eventType: "google.pubsub.topic.v1.publish",