Skip to content
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

updated firebase and firebase-admin #1625

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
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 functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
"dependencies": {
"@google-cloud/firestore": "^5.0.2",
"@google-cloud/pubsub": "^3.0.1",
"@types/node": "^22.6.1",
"axios": "^0.25.0",
"date-fns": "^2.30.0",
"firebase-admin": "^10",
"firebase-functions": "^3.22.0",
"firebase-functions": "^6.0.1",
"fuse.js": "6.5.3",
"handlebars": "^4.7.8",
"lodash": "^4.17.21",
Expand Down
25 changes: 13 additions & 12 deletions functions/src/analysis/updateBillTracker.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { runWith } from "firebase-functions"
import { onDocumentWritten } from "firebase-functions/v2/firestore"

import { isEqual } from "lodash"
import { Bill } from "../bills/types"
import { db, Timestamp } from "../firebase"
Expand All @@ -9,19 +10,18 @@ const currentTrackerVersion = 1
export const billTrackerPath = (billId: string, court: number) =>
`/billTracker/${court}-${billId}`

export const updateBillTracker = runWith({
timeoutSeconds: 10
})
.firestore.document("/generalCourts/{court}/bills/{billId}")
.onWrite(async (change, context) => {
const params = context.params,
// export const updateBillTracker = runWith({
Copy link
Collaborator

Choose a reason for hiding this comment

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

remove

export const updateBillTracker = onDocumentWritten(
"/generalCourts/{court}/bills/{billId}",
async event => {
const params = event.params,
billId = String(params.billId),
court = Number(params.court)
const previousBill = change.before.exists
? Bill.checkWithDefaults(change.before.data())
const previousBill = event.data?.before.exists
? Bill.checkWithDefaults(event.data.before.data())
: undefined
const newBill = change.after.exists
? Bill.checkWithDefaults(change.after.data())
const newBill = event.data?.after.exists
? Bill.checkWithDefaults(event.data.after.data())
: undefined

if (await shouldUpdateBillTracker(newBill, previousBill)) {
Expand All @@ -36,7 +36,8 @@ export const updateBillTracker = runWith({
}
await db.doc(billTrackerPath(billId, court)).set(tracker, { merge: true })
}
})
}
)

async function shouldUpdateBillTracker(
newBill: Bill | undefined,
Expand Down
9 changes: 5 additions & 4 deletions functions/src/auth/createFakeOrg.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import * as functions from "firebase-functions"
import { checkAdmin, checkAuth } from "../common"
import { auth, db } from "../firebase"
import { onCall } from "firebase-functions/v2/https"

// for populating admin module for testing & demonstration
//@TODO: remove

export const createFakeOrg = functions.https.onCall(async (data, context) => {
checkAuth(context, false)
checkAdmin(context)
export const createFakeOrg = onCall(async request => {
checkAuth(request, false)
checkAdmin(request)

const { uid, fullName, email } = data
const { uid, fullName, email } = request.data

const newUser = {
uid,
Expand Down
92 changes: 45 additions & 47 deletions functions/src/auth/createFakeTestimony.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,54 @@
import * as functions from "firebase-functions"
import { checkAdmin, checkAuth } from "../common"
import { auth, db } from "../firebase"
import { Testimony } from "../testimony/types"
import { Timestamp } from "../firebase"
import { onCall } from "firebase-functions/v2/https"

// for populating admin module for testing & demonstration--alert--no auth checked here.
//@TODO: remove

export const createFakeTestimony = functions.https.onCall(
async (data, context) => {
console.log("running fake testimony")
checkAuth(context, false)
checkAdmin(context)

const { uid, fullName, email } = data

const author = {
uid,
fullName,
email,
password: "password",
public: true,
role: "user"
}

await auth.createUser({ uid })

await db.doc(`profiles/${uid}`).set(author)

const id = `${uid}ttmny`

const testimony: Testimony = {
id,
authorUid: author.uid,
authorDisplayName: "none",
authorRole: "user",
billTitle: "An act",
version: 2,
billId: "H1002",
publishedAt: Timestamp.now(),
court: 192,
position: "oppose",
fullName: fullName,
content: fullName + " " + fullName + " " + fullName + " " + fullName,
public: true,
updatedAt: Timestamp.now()
}

const testRef = db.doc(`users/${uid}/publishedTestimony/${id}`)

await testRef.set(testimony)

return { uid: uid, tid: id }
export const createFakeTestimony = onCall(async request => {
console.log("running fake testimony")
checkAuth(request, false)
checkAdmin(request)

const { uid, fullName, email } = request.data

const author = {
uid,
fullName,
email,
password: "password",
public: true,
role: "user"
}

await auth.createUser({ uid })

await db.doc(`profiles/${uid}`).set(author)

const id = `${uid}ttmny`

const testimony: Testimony = {
id,
authorUid: author.uid,
authorDisplayName: "none",
authorRole: "user",
billTitle: "An act",
version: 2,
billId: "H1002",
publishedAt: Timestamp.now(),
court: 192,
position: "oppose",
fullName: fullName,
content: fullName + " " + fullName + " " + fullName + " " + fullName,
public: true,
updatedAt: Timestamp.now()
}
)

const testRef = db.doc(`users/${uid}/publishedTestimony/${id}`)

await testRef.set(testimony)

return { uid: uid, tid: id }
})
8 changes: 4 additions & 4 deletions functions/src/auth/modifyAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ const Request = z.object({
role: ZRole
})

export const modifyAccount = functions.https.onCall(async (data, context) => {
checkAuth(context, false)
checkAdmin(context)
export const modifyAccount = functions.https.onCall(async request => {
checkAuth(request, false)
checkAdmin(request)

const { uid, role } = checkRequestZod(Request, data)
const { uid, role } = checkRequestZod(Request, request.data)

await setRole({ role, auth, db, uid })
})
16 changes: 8 additions & 8 deletions functions/src/bills/BillProcessor.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { QuerySnapshot } from "@google-cloud/firestore"
import { runWith } from "firebase-functions"
import { City } from "../cities/types"
import { Committee } from "../committees/types"
import { DocUpdate } from "../common"
import { db } from "../firebase"
import { Member } from "../members/types"
import { Bill } from "./types"
import { currentGeneralCourt } from "../shared"
import { onMessagePublished } from "firebase-functions/v2/pubsub"
import { onSchedule } from "firebase-functions/v2/scheduler"

export type BillUpdates = Map<string, DocUpdate<Bill>>

Expand All @@ -23,23 +24,22 @@ export default abstract class BillProcessor {
topic: string,
timeoutSeconds = 120
) {
return runWith({ timeoutSeconds })
.pubsub.topic(topic)
.onPublish(async message => {
return onMessagePublished(
{ topic, timeoutSeconds },
async (message: any) => {
if (message.json.run !== true)
throw Error('Expected { "run": true } message')
await new Processor(message.json).run()
})
}
)
}

static scheduled(
Processor: { new (): BillProcessor },
schedule = "every 24 hours",
timeoutSeconds = 120
) {
return runWith({ timeoutSeconds })
.pubsub.schedule(schedule)
.onRun(() => new Processor().run())
return onSchedule({ schedule, timeoutSeconds }, () => new Processor().run())
}

private async run() {
Expand Down
11 changes: 6 additions & 5 deletions functions/src/committees/updateCommitteeRosters.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { runWith } from "firebase-functions"
import { DocUpdate } from "../common"
import { db } from "../firebase"
import { Member } from "../members/types"
import { Committee } from "./types"
import { currentGeneralCourt } from "../shared"
import { onSchedule } from "firebase-functions/v2/scheduler"

/** Updates the list of members in each committee. */
export const updateCommitteeRosters = runWith({ timeoutSeconds: 120 })
.pubsub.schedule("every 24 hours")
.onRun(async () => {
export const updateCommitteeRosters = onSchedule(
{ schedule: "every 24 hours", timeoutSeconds: 120 },
async () => {
const members = await db
.collection(`/generalCourts/${currentGeneralCourt}/members`)
.get()
Expand All @@ -27,7 +27,8 @@ export const updateCommitteeRosters = runWith({ timeoutSeconds: 120 })
)
})
await writer.close()
})
}
)

function computeRosters(members: Member[]) {
const rosters = new Map<string, Member[]>()
Expand Down
10 changes: 5 additions & 5 deletions functions/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,17 @@ export function checkRequestZod<T extends ZodTypeAny>(

/** Return the authenticated user's id or fail if they are not authenticated. */
export function checkAuth(
context: https.CallableContext,
request: https.CallableRequest,
checkEmailVerification = false
) {
const uid = context.auth?.uid
const uid = request.auth?.uid

if (!uid) {
throw fail("unauthenticated", "Caller must be signed in")
}

if (checkEmailVerification && process.env.FUNCTIONS_EMULATOR !== "true") {
const email_verified = context.auth?.token?.email_verified
const email_verified = request.auth?.token?.email_verified

if (!email_verified) {
throw fail("permission-denied", "You must verify an account first")
Expand All @@ -61,8 +61,8 @@ export function checkAuth(
/**
* Checks that the caller is an admin.
*/
export function checkAdmin(context: https.CallableContext) {
const callerRole = context.auth?.token.role
export function checkAdmin(request: https.CallableRequest) {
const callerRole = request.auth?.token.role
if (callerRole !== "admin") {
throw fail("permission-denied", "You must be an admin")
}
Expand Down
12 changes: 8 additions & 4 deletions functions/src/events/scrapeEvents.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { runWith } from "firebase-functions"
import { DateTime } from "luxon"
import { logFetchError } from "../common"
import { db, Timestamp } from "../firebase"
Expand All @@ -15,6 +14,7 @@ import {
SpecialEventContent
} from "./types"
import { currentGeneralCourt } from "../shared"
import { onSchedule } from "firebase-functions/scheduler"

abstract class EventScraper<ListItem, Event extends BaseEvent> {
private schedule
Expand All @@ -26,9 +26,13 @@ abstract class EventScraper<ListItem, Event extends BaseEvent> {
}

get function() {
return runWith({ timeoutSeconds: this.timeout })
.pubsub.schedule(this.schedule)
.onRun(() => this.run())
return onSchedule(
{
schedule: this.schedule,
timeoutSeconds: this.timeout
},
() => this.run()
)
}

abstract listEvents(): Promise<ListItem[]>
Expand Down
11 changes: 6 additions & 5 deletions functions/src/members/createMemberSearchIndex.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { pubsub } from "firebase-functions"
import { db } from "../firebase"
import { currentGeneralCourt } from "../shared"
import { onSchedule } from "firebase-functions/scheduler"

/** Create a document that aggregates all legislative members for easier
* searching on the client. */
export const createMemberSearchIndex = pubsub
.schedule("every 24 hours")
.onRun(async () => {
export const createMemberSearchIndex = onSchedule(
"every 24 hours",
async () => {
const members = await db
.collection(`/generalCourts/${currentGeneralCourt}/members`)
.get()
Expand All @@ -24,4 +24,5 @@ export const createMemberSearchIndex = pubsub
representatives: index.filter(d => d.Branch === "House"),
senators: index.filter(d => d.Branch === "Senate")
})
})
}
)
11 changes: 6 additions & 5 deletions functions/src/notifications/cleanupNotifications.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import * as functions from "firebase-functions"
import * as admin from "firebase-admin"
import { Timestamp } from "../firebase"
import { onSchedule } from "firebase-functions/v2/scheduler"

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

// Define the cleanupNotifications function
export const cleanupNotifications = functions.pubsub
.schedule("every 24 hours")
.onRun(async context => {
export const cleanupNotifications = onSchedule(
"every 24 hours",
async context => {
// Define the time threshold for old notifications, topic events, and email documents
const retentionPeriodDays = 60 // Adjust this value as needed
const threshold = new Timestamp(
Expand Down Expand Up @@ -46,4 +46,5 @@ export const cleanupNotifications = functions.pubsub

const deleteEmailPromises = emailsSnapshot.docs.map(doc => doc.ref.delete())
await Promise.all(deleteEmailPromises)
})
}
)
Loading