Skip to content

Upgrade Firebase to Node 20 #1848

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Aug 13, 2025
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
2 changes: 1 addition & 1 deletion .github/actions/setup-repo/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ inputs:
node-version:
description: "Version used by actions/setup-node"
required: true
default: "18"
default: "20"
yarn-version:
description: "Version of Yarn used to install dependencies"
required: true
Expand Down
4 changes: 2 additions & 2 deletions components/ProfilePage/SelectLegislators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const LegislatorForm: React.FC<
<Form.Label>{t("legislator.representative")}</Form.Label>
<Search
placeholder={t("legislator.searchRepresentative")}
index={index.representatives}
index={index?.representatives}
isLoading={profile.updatingRep}
memberId={profile.profile?.representative?.id}
update={profile.updateRep}
Expand All @@ -47,7 +47,7 @@ export const LegislatorForm: React.FC<
<Form.Label>{t("legislator.senator")}</Form.Label>
<Search
placeholder={t("legislator.searchSenator")}
index={index.senators}
index={index?.senators}
isLoading={profile.updatingSenator}
memberId={profile.profile?.senator?.id}
update={profile.updateSenator}
Expand Down
2 changes: 1 addition & 1 deletion components/legislatorSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const useSearch = <T extends MemberSearchIndexItem, M extends boolean>(
index: T[]
) => {
const byId = useMemo(
() => Object.fromEntries(index.map(m => [m.MemberCode, m])),
() => (index ? Object.fromEntries(index?.map(m => [m.MemberCode, m])) : {}),
[index]
)

Expand Down
2 changes: 1 addition & 1 deletion firebase.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"predeploy": ["yarn build:functions"],
"source": "functions",
"codebase": "maple",
"runtime": "nodejs18",
"runtime": "nodejs20",
"runtimeConfig": ".runtimeconfig.json"
},
{
Expand Down
10 changes: 9 additions & 1 deletion functions/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: "ts-jest",
testEnvironment: "node"
testEnvironment: "node",
moduleNameMapper: {
// This maps firebase-admin/auth to the correct location within node_modules
"^firebase-admin/auth$":
"<rootDir>/node_modules/firebase-admin/lib/auth/index.js",
"^firebase-admin/app$":
"<rootDir>/node_modules/firebase-admin/lib/app/index.js",
"^firebase-admin/(.*)$": "<rootDir>/node_modules/firebase-admin/lib/$1"
}
}
6 changes: 3 additions & 3 deletions functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
"assemblyai": "^4.9.0",
"axios": "^0.25.0",
"date-fns": "^2.30.0",
"firebase-admin": "^10",
"firebase-functions": "^3.22.0",
"firebase-admin": "^11.11.1",
"firebase-functions": "^5.1.1",
"fluent-ffmpeg": "^2.1.3",
"fuse.js": "6.5.3",
"handlebars": "^4.7.8",
Expand All @@ -42,7 +42,7 @@
"@types/object-hash": "^2.2.1",
"copyfiles": "^2.4.1",
"firebase-functions-test": "^0.3.3",
"firebase-tools": "^11.16.0",
"firebase-tools": "^12.4.0",
"jest": "^27.5.1",
"rimraf": "^3.0.2",
"ts-jest": "^27.1.3",
Expand Down
2 changes: 1 addition & 1 deletion functions/src/auth/setRole.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { UserRecord } from "firebase-admin/lib/auth/user-record"
import { UserRecord } from "firebase-admin/auth"
import { Undefined } from "runtypes"
import { Profile } from "../profile/types"
import { Auth, Database } from "../types"
Expand Down
4 changes: 2 additions & 2 deletions functions/src/events/scrapeEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { DateTime } from "luxon"
import { JSDOM } from "jsdom"
import { AssemblyAI } from "assemblyai"
import { logFetchError } from "../common"
import { admin, db, Timestamp } from "../firebase"
import { db, storage, Timestamp } from "../firebase"
import * as api from "../malegislature"
import {
BaseEvent,
Expand Down Expand Up @@ -182,7 +182,7 @@ const extractAudioFromVideo = async (
})

// Upload the audio file
const bucket = admin.storage().bucket()
const bucket = storage.bucket()
const audioFileName = `hearing-${EventId}-${Date.now()}.m4a`
const file = bucket.file(audioFileName)

Expand Down
47 changes: 27 additions & 20 deletions functions/src/firebase.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
import * as admin from "firebase-admin"
import { initializeApp, App } from "firebase-admin/app"
import { getAuth } from "firebase-admin/auth"
import {
getFirestore,
Timestamp,
FieldValue,
FieldPath,
DocumentData,
QueryDocumentSnapshot,
DocumentSnapshot,
QuerySnapshot
} from "firebase-admin/firestore"
import { getStorage } from "firebase-admin/storage"

export const app: App = initializeApp()
export const db = getFirestore(app)
export const storage = getStorage(app)
export const auth = getAuth(app)

admin.initializeApp()
export const db = admin.firestore()
export const storage = admin.storage()
export const auth = admin.auth()
export { admin }
// Gotta use the same Timestamp class as the admin package.
export const Timestamp = admin.firestore.Timestamp
export type Timestamp = admin.firestore.Timestamp
export const FieldValue = admin.firestore.FieldValue
export type FieldValue = admin.firestore.FieldValue
export const FieldPath = admin.firestore.FieldPath
export type FieldPath = admin.firestore.FieldPath
export type DocumentData = admin.firestore.DocumentData
export type QueryDocumentSnapshot = admin.firestore.QueryDocumentSnapshot
export type DocumentSnapshot = admin.firestore.DocumentSnapshot
export type QuerySnapshot = admin.firestore.QuerySnapshot
export { Timestamp, FieldValue, FieldPath }
export type {
DocumentData,
QueryDocumentSnapshot,
DocumentSnapshot,
QuerySnapshot
}

// Extreme hack to extract the File type from the admin storage package. For
// some reason admin.storage does not resolve the storage namespace, as
// admin.firestore does.
// Extreme hack to extract the File type from the admin storage package.
export type File = ReturnType<
ReturnType<ReturnType<typeof admin.storage>["bucket"]>["file"]
ReturnType<ReturnType<typeof getStorage>["bucket"]>["file"]
>
4 changes: 2 additions & 2 deletions functions/src/notifications/cleanupNotifications.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import * as functions from "firebase-functions"
import * as admin from "firebase-admin"
import { getFirestore } from "firebase-admin/firestore"
import { Timestamp } from "../firebase"

const RETENTION_PERIOD_DAYS = 60

// Get a reference to the Firestore database
const db = admin.firestore()
const db = getFirestore()

// TODO
// 1.) Do we actually want to delete old notifications? (check with Matt V)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

// Import necessary Firebase modules
import * as functions from "firebase-functions"
import * as admin from "firebase-admin"
import { getFirestore } from "firebase-admin/firestore"
import { Timestamp } from "../firebase"
import { BillHistoryUpdateNotification } from "./types"

// Get a reference to the Firestore database
const db = admin.firestore()
const db = getFirestore()

// Define the populateBillNotificationEvents function
export const populateBillHistoryNotificationEvents = functions.firestore
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

// Import necessary Firebase modules
import * as functions from "firebase-functions"
import * as admin from "firebase-admin"
import { getFirestore } from "firebase-admin/firestore"
import { Timestamp } from "../firebase"
import { TestimonySubmissionNotification } from "./types"

// Get a reference to the Firestore database
const db = admin.firestore()
const db = getFirestore()

// Define the populateOrgNotificationEvents function
export const populateTestimonySubmissionNotificationEvents = functions.firestore
Expand Down
4 changes: 2 additions & 2 deletions functions/src/notifications/publishNotifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

// Import necessary Firebase modules
import * as functions from "firebase-functions"
import * as admin from "firebase-admin"
import { getFirestore } from "firebase-admin/firestore"
import { Timestamp } from "../firebase"
import {
BillHistoryUpdateNotification,
Expand All @@ -16,7 +16,7 @@ import {
import { cloneDeep } from "lodash"

// Get a reference to the Firestore database
const db = admin.firestore()
const db = getFirestore()

const createNotificationFields = (
entity: BillHistoryUpdateNotification | TestimonySubmissionNotification
Expand Down
11 changes: 6 additions & 5 deletions functions/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type * as firebase from "./firebase"
import { auth, db } from "./firebase"
import type * as api from "./malegislature"
import { app } from "./firebase"

export type Database = typeof firebase.db
export type Admin = typeof firebase.admin
export type Auth = typeof firebase.auth
export type Firebase = typeof firebase
export type Database = typeof db
export type Admin = typeof auth
export type Auth = typeof auth
export type Firebase = typeof app
export type Api = typeof api

export type Context = {
Expand Down
Loading