Skip to content
Open
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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ jobs:

steps:
- uses: actions/checkout@v4
- name: Use Node.js 20
- name: Use Node.js 22
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22
- name: Use Java 21
uses: actions/setup-java@v4
with:
Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,18 @@ Stream is the #1 provider for activity feeds and chat APIs, powering over a bill

- [Firebase project](https://firebase.google.com/docs/projects/create) with Blaze (pay-as-you-go) plan
- [Stream account](https://getstream.io/try-for-free/) with API key and secret
- Node.js 22 for local builds and integration tests (`.nvmrc` is included)

### Installation

Each extension can be installed using either the Firebase Console or the Firebase CLI. See individual extension READMEs for detailed installation instructions.

### Runtime Strategy

These extensions now target the `nodejs22` runtime, but they intentionally stay on `firebase-functions/v1`.

Firebase Extensions currently treats the trigger types used here as 1st-gen only, so the repo avoids mixing in partial v2 migrations that would create versioning and rollout ambiguity for installed extension instances.

## Resources

- [Stream Activity Feeds Documentation](https://getstream.io/activity-feeds/docs/)
Expand Down
6 changes: 6 additions & 0 deletions auth-activity-feeds/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## Version 0.2.5

- Update all extension runtimes and package engines to Node v22
- Keep the extension on `firebase-functions/v1`, because Firebase Extensions still treats these Auth and callable triggers as 1st-gen only
- Align CI and local development on Node v22

## Version 0.2.4

- Ensure all cloud functions run on Node v18
Expand Down
8 changes: 4 additions & 4 deletions auth-activity-feeds/extension.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: auth-activity-feeds
version: 0.2.4
version: 0.2.5
specVersion: v1beta # Firebase Extensions specification version (do not edit)

displayName: Authenticate with Stream Feeds
Expand Down Expand Up @@ -101,21 +101,21 @@ resources:
type: firebaseextensions.v1beta.function
properties:
location: ${LOCATION}
runtime: nodejs18
runtime: nodejs22
eventTrigger:
eventType: providers/firebase.auth/eventTypes/user.create
resource: projects/${PROJECT_ID}
- name: deleteStreamUser
type: firebaseextensions.v1beta.function
properties:
location: ${LOCATION}
runtime: nodejs18
runtime: nodejs22
eventTrigger:
eventType: providers/firebase.auth/eventTypes/user.delete
resource: projects/${PROJECT_ID}
- name: getStreamUserToken
type: firebaseextensions.v1beta.function
properties:
location: ${LOCATION}
runtime: nodejs18
runtime: nodejs22
httpsTrigger: {}
18 changes: 1 addition & 17 deletions auth-activity-feeds/functions/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion auth-activity-feeds/functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"logs": "firebase functions:log"
},
"engines": {
"node": "18"
"node": "22"
},
"main": "lib/index.js",
"dependencies": {
Expand Down
6 changes: 6 additions & 0 deletions auth-chat/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## Version 0.2.5

- Update all extension runtimes and package engines to Node v22
- Keep the extension on `firebase-functions/v1`, because Firebase Extensions still treats these Auth and callable triggers as 1st-gen only
- Align CI and local development on Node v22

## Version 0.2.4

- Ensure all cloud functions run on Node v18
Expand Down
10 changes: 5 additions & 5 deletions auth-chat/extension.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: auth-chat
version: 0.2.4
version: 0.2.5
specVersion: v1beta # Firebase Extensions specification version (do not edit)

displayName: Authenticate with Stream Chat
Expand Down Expand Up @@ -83,27 +83,27 @@ resources:
type: firebaseextensions.v1beta.function
properties:
location: ${LOCATION}
runtime: nodejs18
runtime: nodejs22
eventTrigger:
eventType: providers/firebase.auth/eventTypes/user.create
resource: projects/${PROJECT_ID}
- name: deleteStreamUser
type: firebaseextensions.v1beta.function
properties:
location: ${LOCATION}
runtime: nodejs18
runtime: nodejs22
eventTrigger:
eventType: providers/firebase.auth/eventTypes/user.delete
resource: projects/${PROJECT_ID}
- name: getStreamUserToken
type: firebaseextensions.v1beta.function
properties:
location: ${LOCATION}
runtime: nodejs18
runtime: nodejs22
httpsTrigger: {}
- name: revokeStreamUserToken
type: firebaseextensions.v1beta.function
properties:
location: ${LOCATION}
runtime: nodejs18
runtime: nodejs22
httpsTrigger: {}
18 changes: 1 addition & 17 deletions auth-chat/functions/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion auth-chat/functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
"prepare": "npm run build",
"lint": "ESLINT_USE_FLAT_CONFIG=false eslint .",
"build": "tsc",
"test": "npm run build && node --test test/*.test.js",
"serve": "npm run build && firebase emulators:start --only functions",
"shell": "npm run build && firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "18"
"node": "22"
},
"main": "lib/index.js",
"dependencies": {
Expand Down
145 changes: 145 additions & 0 deletions auth-chat/functions/test/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
"use strict";

const assert = require("node:assert/strict");
const {after, test} = require("node:test");
const Module = require("node:module");
const firebaseFunctionsTest = require("firebase-functions-test");

const fft = firebaseFunctionsTest({
projectId: "demo-stream",
});

function loadFunctionsWithMock(mockClient) {
process.env.STREAM_API_KEY = "test-key";
process.env.STREAM_API_SECRET = "test-secret";

const originalLoad = Module._load;
const indexPath = require.resolve("../lib/index.js");

delete require.cache[indexPath];

Module._load = function(request, parent, isMain) {
if (request === "firebase-admin") {
return {
initializeApp() {},
};
}

if (request === "stream-chat") {
return {
StreamChat: {
getInstance() {
return mockClient;
},
},
};
}

return originalLoad.call(this, request, parent, isMain);
};

try {
return require(indexPath);
} finally {
Module._load = originalLoad;
}
}

after(() => {
fft.cleanup();
});

test("createStreamUser upserts the mapped Stream user", async () => {
const calls = [];
const mockResponse = {created: true};
const functionsModule = loadFunctionsWithMock({
async upsertUser(user) {
calls.push(user);
return mockResponse;
},
async deleteUser() {
throw new Error("deleteUser should not be called");
},
createToken() {
throw new Error("createToken should not be called");
},
revokeUserToken() {
throw new Error("revokeUserToken should not be called");
},
});

const wrapped = fft.wrap(functionsModule.createStreamUser);
const userRecord = fft.auth.makeUserRecord({
uid: "user-123",
displayName: "John Doe",
email: "user@example.com",
photoURL: "https://example.com/johndoe.jpg",
});

const response = await wrapped(userRecord);

assert.deepStrictEqual(calls, [
{
id: "user-123",
name: "John Doe",
email: "user@example.com",
image: "https://example.com/johndoe.jpg",
},
]);
assert.deepStrictEqual(response, mockResponse);
});

test("createStreamUser surfaces Stream client failures", async () => {
const expectedError = new Error("Stream unavailable");
const functionsModule = loadFunctionsWithMock({
async upsertUser() {
throw expectedError;
},
async deleteUser() {
throw new Error("deleteUser should not be called");
},
createToken() {
throw new Error("createToken should not be called");
},
revokeUserToken() {
throw new Error("revokeUserToken should not be called");
},
});

const wrapped = fft.wrap(functionsModule.createStreamUser);
const userRecord = fft.auth.makeUserRecord({
uid: "user-123",
});

await assert.rejects(wrapped(userRecord), expectedError);
});

test("deleteStreamUser deletes the mapped Stream user", async () => {
const calls = [];
const mockResponse = {deleted: true};
const functionsModule = loadFunctionsWithMock({
async upsertUser() {
throw new Error("upsertUser should not be called");
},
async deleteUser(userId) {
calls.push(userId);
return mockResponse;
},
createToken() {
throw new Error("createToken should not be called");
},
revokeUserToken() {
throw new Error("revokeUserToken should not be called");
},
});

const wrapped = fft.wrap(functionsModule.deleteStreamUser);
const userRecord = fft.auth.makeUserRecord({
uid: "user-123",
});

const response = await wrapped(userRecord);

assert.deepStrictEqual(calls, ["user-123"]);
assert.deepStrictEqual(response, mockResponse);
});
6 changes: 6 additions & 0 deletions firestore-activity-feeds/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## Version 0.2.5

- Update the extension runtime and package engine to Node v22
- Keep the Firestore trigger on `firebase-functions/v1`, because Firebase Extensions still treats this trigger type as 1st-gen only
- Align CI and local development on Node v22

## Version 0.2.4

- Ensure all cloud functions run on Node v18
Expand Down
4 changes: 2 additions & 2 deletions firestore-activity-feeds/extension.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: firestore-activity-feeds
version: 0.2.4
version: 0.2.5
specVersion: v1beta # Firebase Extensions specification version (do not edit)

displayName: Sync Firestore with Activity Feeds
Expand Down Expand Up @@ -89,7 +89,7 @@ resources:
type: firebaseextensions.v1beta.function
properties:
location: ${LOCATION}
runtime: nodejs18
runtime: nodejs22
eventTrigger:
eventType: providers/cloud.firestore/eventTypes/document.write
resource: projects/${PROJECT_ID}/databases/(default)/documents/${COLLECTION}/{feedId}/{userId}/{foreignId}
Loading
Loading