diff --git a/apps/account-functions/BUILD.bazel b/apps/account-functions/BUILD.bazel deleted file mode 100644 index b2be783b6..000000000 --- a/apps/account-functions/BUILD.bazel +++ /dev/null @@ -1,30 +0,0 @@ -load("//tools:defaults.bzl", "esbuild", "ts_library") - -package(default_visibility = ["//visibility:private"]) - -ts_library( - name = "accounts", - srcs = [ - "before-create.ts", - "before-sign-in.ts", - "index.ts", - ], - visibility = [ - "//apps/functions:__pkg__", - ], - deps = [ - "@npm//gcip-cloud-functions", - ], -) - -esbuild( - name = "accounts_compiled", - entry_points = [ - "index.ts", - ], - format = "esm", - visibility = ["//apps:__pkg__"], - deps = [ - ":accounts", - ], -) diff --git a/apps/account-functions/before-create.ts b/apps/account-functions/before-create.ts deleted file mode 100644 index a922d0f4a..000000000 --- a/apps/account-functions/before-create.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {Auth, https, UserRecord} from 'gcip-cloud-functions'; - -/** - * Validate accounts before their creation using google cloud before create - * synchronous function. - */ -export const beforeCreate = new Auth().functions().beforeCreateHandler((user: UserRecord) => { - if (user.email && !user.email.endsWith('@google.com')) { - throw new https.HttpsError('invalid-argument', `Unauthorized email "${user.email}"`); - } - - return {}; -}); diff --git a/apps/account-functions/before-sign-in.ts b/apps/account-functions/before-sign-in.ts deleted file mode 100644 index efb4d8132..000000000 --- a/apps/account-functions/before-sign-in.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - Auth, - https, - UserRecord, - UserEventUpdateRequest, - AuthEventContext, -} from 'gcip-cloud-functions'; - -/** Validate accounts before sign in using google cloud before sigin in syncronous function. */ - -export const beforeSignIn = new Auth() - .functions() - .beforeSignInHandler( - async (user: UserRecord, context: AuthEventContext): Promise => { - /** The UserEventUpdate to save based on the signin results. */ - const event: UserEventUpdateRequest = {}; - - // If a user is able to reach this without a login credential being present, throw an auth error. - // Note: This should not be possible, but it doesn't hurt to have this check in place. - if (context.credential === undefined) { - throw new https.HttpsError( - 'unauthenticated', - `Cannot sign in as '${user.email}' without credential.`, - ); - } - - // When users sign in with github, we save the access token as a claim on the user object. - if (context.credential.providerId === 'github.com') { - event.customClaims = {...event.customClaims, githubToken: context.credential.accessToken}; - } - - return event; - }, - ); diff --git a/apps/account-functions/index.ts b/apps/account-functions/index.ts deleted file mode 100644 index a0ddb5287..000000000 --- a/apps/account-functions/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {beforeCreate} from './before-create.js'; -export {beforeSignIn} from './before-sign-in.js'; diff --git a/apps/code-of-conduct/app/BUILD.bazel b/apps/code-of-conduct/app/BUILD.bazel index 7cb4cabea..78a4b4658 100644 --- a/apps/code-of-conduct/app/BUILD.bazel +++ b/apps/code-of-conduct/app/BUILD.bazel @@ -1,5 +1,5 @@ -load("//tools:defaults.bzl", "ng_module") load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tools:defaults.bzl", "ng_module") package(default_visibility = ["//apps/code-of-conduct:__subpackages__"]) @@ -13,9 +13,9 @@ ng_module( "app.component.html", ], deps = [ + "//apps/code-of-conduct/app/account", "//apps/code-of-conduct/app/block-user", "//apps/code-of-conduct/app/user-table", - "//apps/shared/account", "@npm//@angular/common", "@npm//@angular/core", "@npm//@angular/router", @@ -47,9 +47,9 @@ ng_module( "app.routes.ts", ], deps = [ + "//apps/code-of-conduct/app/account", "//apps/code-of-conduct/app/login", "//apps/code-of-conduct/app/main", - "//apps/shared/account", "@npm//@angular/router", ], ) diff --git a/apps/shared/account/BUILD.bazel b/apps/code-of-conduct/app/account/BUILD.bazel similarity index 100% rename from apps/shared/account/BUILD.bazel rename to apps/code-of-conduct/app/account/BUILD.bazel index 02c3f7b17..a954bdbf6 100644 --- a/apps/shared/account/BUILD.bazel +++ b/apps/code-of-conduct/app/account/BUILD.bazel @@ -1,5 +1,5 @@ -load("//tools:defaults.bzl", "ng_module") load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tools:defaults.bzl", "ng_module") ng_module( name = "account", diff --git a/apps/shared/account/account.component.html b/apps/code-of-conduct/app/account/account.component.html similarity index 100% rename from apps/shared/account/account.component.html rename to apps/code-of-conduct/app/account/account.component.html diff --git a/apps/shared/account/account.component.scss b/apps/code-of-conduct/app/account/account.component.scss similarity index 100% rename from apps/shared/account/account.component.scss rename to apps/code-of-conduct/app/account/account.component.scss diff --git a/apps/shared/account/account.component.ts b/apps/code-of-conduct/app/account/account.component.ts similarity index 100% rename from apps/shared/account/account.component.ts rename to apps/code-of-conduct/app/account/account.component.ts diff --git a/apps/shared/account/account.guard.ts b/apps/code-of-conduct/app/account/account.guard.ts similarity index 100% rename from apps/shared/account/account.guard.ts rename to apps/code-of-conduct/app/account/account.guard.ts diff --git a/apps/shared/account/account.service.ts b/apps/code-of-conduct/app/account/account.service.ts similarity index 100% rename from apps/shared/account/account.service.ts rename to apps/code-of-conduct/app/account/account.service.ts diff --git a/apps/shared/account/index.ts b/apps/code-of-conduct/app/account/index.ts similarity index 100% rename from apps/shared/account/index.ts rename to apps/code-of-conduct/app/account/index.ts diff --git a/apps/code-of-conduct/app/app.component.ts b/apps/code-of-conduct/app/app.component.ts index f371f19da..9b2681e12 100644 --- a/apps/code-of-conduct/app/app.component.ts +++ b/apps/code-of-conduct/app/app.component.ts @@ -1,7 +1,7 @@ import {Component} from '@angular/core'; import {MatToolbarModule} from '@angular/material/toolbar'; import {RouterModule} from '@angular/router'; -import {AccountComponent} from '../../shared/account/account.component.js'; +import {AccountComponent} from './account/account.component.js'; import {BlockUserComponent} from './block-user/block-user.component.js'; import {UserTableComponent} from './user-table/user-table.component.js'; @@ -10,12 +10,6 @@ import {UserTableComponent} from './user-table/user-table.component.js'; selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], - imports: [ - MatToolbarModule, - RouterModule, - BlockUserComponent, - AccountComponent, - UserTableComponent, - ], + imports: [MatToolbarModule, RouterModule, AccountComponent], }) export class AppComponent {} diff --git a/apps/code-of-conduct/app/app.routes.ts b/apps/code-of-conduct/app/app.routes.ts index 52d2230cb..0d69ef5f3 100644 --- a/apps/code-of-conduct/app/app.routes.ts +++ b/apps/code-of-conduct/app/app.routes.ts @@ -1,5 +1,5 @@ import {Routes} from '@angular/router'; -import {isLoggedInGuard} from '../../shared/account/account.guard.js'; +import {isLoggedInGuard} from './account/account.guard.js'; export const routes: Routes = [ { diff --git a/apps/code-of-conduct/app/login/BUILD.bazel b/apps/code-of-conduct/app/login/BUILD.bazel index 3e5e8d8ed..d90980b3c 100644 --- a/apps/code-of-conduct/app/login/BUILD.bazel +++ b/apps/code-of-conduct/app/login/BUILD.bazel @@ -1,5 +1,5 @@ -load("//tools:defaults.bzl", "ng_module") load("@io_bazel_rules_sass//:defs.bzl", "sass_binary") +load("//tools:defaults.bzl", "ng_module") package(default_visibility = ["//apps/code-of-conduct:__subpackages__"]) @@ -11,7 +11,7 @@ ng_module( "login.component.html", ], deps = [ - "//apps/shared/account", + "//apps/code-of-conduct/app/account", "@npm//@angular/common", "@npm//@angular/core", "@npm//@angular/material", diff --git a/apps/code-of-conduct/app/login/login.component.ts b/apps/code-of-conduct/app/login/login.component.ts index c37b3e7a1..f899de219 100644 --- a/apps/code-of-conduct/app/login/login.component.ts +++ b/apps/code-of-conduct/app/login/login.component.ts @@ -3,7 +3,7 @@ import {Component, inject, NgZone} from '@angular/core'; import {MatButtonModule} from '@angular/material/button'; import {MatCardModule} from '@angular/material/card'; import {Router} from '@angular/router'; -import {AccountService} from '../../../shared/account/account.service.js'; +import {AccountService} from '../account/account.service.js'; @Component({ standalone: true, diff --git a/apps/credential-service/BUILD.bazel b/apps/credential-service/BUILD.bazel deleted file mode 100644 index dd93b7e7e..000000000 --- a/apps/credential-service/BUILD.bazel +++ /dev/null @@ -1,45 +0,0 @@ -load("//tools:defaults.bzl", "esbuild_esm_bundle") -load("@build_bazel_rules_nodejs//:index.bzl", "copy_to_bin", "nodejs_binary") - -nodejs_binary( - name = "serve", - data = [ - ":bin_files", - ], - entry_point = ":credential-service.js", -) - -copy_to_bin( - name = "bin_files", - srcs = [ - "Dockerfile", - "package.json", - ], -) - -esbuild_esm_bundle( - name = "credential-service", - entry_point = "//apps/credential-service/lib:server.ts", - external = [ - # Mark farmhash as external as it loads .node files directly, which are unused in our usage - "farmhash", - ], - target = "node16", - visibility = [ - "//apps/credential-service:__subpackages__", - ], - deps = [ - "//apps/credential-service/lib", - ], -) - -sh_binary( - name = "deploy", - srcs = [ - "deploy.sh", - ], - data = [ - ":bin_files", - ":credential-service.js", - ], -) diff --git a/apps/credential-service/Dockerfile b/apps/credential-service/Dockerfile deleted file mode 100644 index 0870d1ae1..000000000 --- a/apps/credential-service/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM node:16-slim@sha256:3ebf2875c188d22939c6ab080cfb1a4a6248cc86bae600ea8e2326aa03acdb8f - -WORKDIR /usr/src/app - -COPY package.json ./ - -COPY credential-service.js ./ - -EXPOSE 8080 - -CMD ["node", "credential-service.js"] diff --git a/apps/credential-service/deploy.sh b/apps/credential-service/deploy.sh deleted file mode 100755 index d7930749b..000000000 --- a/apps/credential-service/deploy.sh +++ /dev/null @@ -1,9 +0,0 @@ -if [ -z $(which gcloud) ]; then - echo "###################################################################" - echo "# Failed: #" - echo "# gcloud must be installed in order to deploy this service #" - echo "###################################################################" - exit 1; -fi - -gcloud run deploy credential-service --platform=managed --region=us-central1 --source $(dirname "$0") diff --git a/apps/credential-service/lib/BUILD.bazel b/apps/credential-service/lib/BUILD.bazel deleted file mode 100644 index 7fe0a030b..000000000 --- a/apps/credential-service/lib/BUILD.bazel +++ /dev/null @@ -1,23 +0,0 @@ -load("//tools:defaults.bzl", "ts_library") - -exports_files([ - "server.ts", -]) - -ts_library( - name = "lib", - srcs = [ - "server.ts", - ], - visibility = [ - "//apps/credential-service:__pkg__", - ], - deps = [ - "@npm//@octokit/auth-app", - "@npm//@octokit/rest", - "@npm//@types/node", - "@npm//@types/ws", - "@npm//firebase-admin", - "@npm//ws", - ], -) diff --git a/apps/credential-service/lib/server.ts b/apps/credential-service/lib/server.ts deleted file mode 100644 index 787829340..000000000 --- a/apps/credential-service/lib/server.ts +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env node - -import {createServer, IncomingHttpHeaders, IncomingMessage} from 'http'; -import {WebSocketServer, WebSocket} from 'ws'; -import {Octokit} from '@octokit/rest'; -import {createAppAuth} from '@octokit/auth-app'; -import {Duplex} from 'stream'; -import admin, {AppOptions} from 'firebase-admin'; -import assert from 'assert'; - -/** The temporary access token and a convience method for revoking it. */ -interface AccessTokenAndRevocation { - token: string; - revokeToken: () => void; -} - -interface RequestParameterHeaders extends IncomingHttpHeaders { - ng_repo_name: string; - ng_repo_owner: string; -} - -/** Regex for matching authorization header uses. */ -const authorizationRegex = new RegExp(/Bearer (.*)/); -/** - * The length of time in ms between heartbeat checks. - * - * 10 seconds is used as during the local PR validation process, the javascript thread - * becomes blocked, and can cause a timeout. - */ -const heartBeatIntervalLength = 10000; -/** The port to bind the server to */ -const PORT = 8080; -/** The ID of the Github app used to generating tokens. */ -assert(process.env.GITHUB_APP_ID, 'GITHUB_APP_ID is not defined in the environment'); -const GITHUB_APP_ID = process.env.GITHUB_APP_ID; -/** - * The PEM key of the Github app used to generating tokens, the value is stored in the environment - * variable in Base64 to make it easier to manage it being a multiline string. - */ -assert( - process.env.GITHUB_APP_PEM_BASE64, - 'GITHUB_APP_PEM_BASE64 is not defined in the environment', -); -const GITHUB_APP_PEM = Buffer.from(process.env.GITHUB_APP_PEM_BASE64, 'base64').toString('utf-8'); -/** The firebase confgiuration for the firebase application being used for authentication. */ -assert(process.env.FIREBASE_APP_CONFIG, 'FIREBASE_APP_CONFIG is not defined in the environment'); -const FIREBASE_APP_CONFIG = JSON.parse(process.env.FIREBASE_APP_CONFIG) as AppOptions; - -// Initialize the Firebase application. -admin.initializeApp(FIREBASE_APP_CONFIG); - -/** Generate a temporary access token with access to the requested repository. */ -export async function generateAccessToken( - owner: string, - repo: string, -): Promise { - /** The github client used for generating the token. */ - const github = new Octokit({ - authStrategy: createAppAuth, - auth: {appId: GITHUB_APP_ID, privateKey: GITHUB_APP_PEM}, - }); - /** The specific installation id for the provided repository. */ - const {id: installation_id} = (await github.apps.getRepoInstallation({owner, repo})).data; - /** A temporary github access token. */ - const {token} = (await github.rest.apps.createInstallationAccessToken({installation_id})).data; - - return { - token, - revokeToken: async () => await new Octokit({auth: token}).apps.revokeInstallationAccessToken(), - }; -} - -/** - * WebSocket handler to generate temporary Github access token. - * - * The access token is automatically revoked when the websocket is closed. - */ -async function wsHandler(ws: WebSocket, req: IncomingMessage) { - /** Whether the websocket has confirmed the heartbeat response since the most recent check. */ - let receivedHeartbeatResponse: boolean; - /** The interval instance for checking the heartbeat. */ - let heartbeatInterval = setInterval(checkHeartbeat, heartBeatIntervalLength); - /** - * The repository name and owner for the request. - * Note: We safely case the header type as having these fields because they are checked prior - * to the WebSocket handler function being invoked. - */ - const {ng_repo_owner: owner, ng_repo_name: repo} = req.headers as RequestParameterHeaders; - - /** The temporary Github access token and function to revoke the token.. */ - const {token, revokeToken} = await generateAccessToken(owner, repo); - /** Check to make sure the heartbeat is still alive. */ - function checkHeartbeat() { - if (receivedHeartbeatResponse === false) { - ws.close(1008, 'Cannot find socket via heartbeat check'); - ws.terminate(); - } - receivedHeartbeatResponse = false; - ws.ping(); - } - - /** - * Cleans up the state of the function when the WebSocket is completed. - */ - async function complete() { - await revokeToken(); - clearInterval(heartbeatInterval); - } - - // Ensure that cleanup is done when the WebSocket closes. - ws.on('close', complete); - ws.on('error', complete); - - // Handle the pong response from the websocket client, updating the heartbeat as alive. - ws.on('pong', () => (receivedHeartbeatResponse = true)); - - // Send the temporary Github token to the websocket client - ws.send(token); -} - -/** - * Handle upgrade requests before the websocket is used. Enforces authentication mechanisms - * and ensuring the required data is present in the request. - */ -async function upgradeHandler(req: IncomingMessage, socket: Duplex, head: Buffer) { - try { - assert(req.headers.authorization, Error('Missing authorization header')); - if (!authorizationRegex.test(req.headers.authorization)) { - throw Error('Invalid authorization header syntax'); - } - /** - * The NgDev token from the user to be verified. - * We use a non-null assertion as the regex was already tested above. - */ - const [_, ngDevToken] = authorizationRegex.exec(req.headers.authorization)!; - await admin - .auth() - .verifySessionCookie(ngDevToken, /* checkRevoked */ true) - .then((decodedToken: admin.auth.DecodedIdToken) => { - console.log(`Verified login of ${decodedToken.email}`); - }); - } catch (e) { - console.error('Unable to verified authorized user'); - console.error(e); - socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n'); - socket.destroy(); - return; - } - - /** The repository name and owner for the request. */ - const {ng_repo_name: repo, ng_repo_owner: owner} = req.headers as RequestParameterHeaders; - - if (!repo || !owner) { - console.error('Missing a repo or owner parameter'); - socket.write('HTTP/1.1 400 Bad Reqest\r\n\r\n'); - socket.destroy(); - return; - } - - wss.handleUpgrade(req, socket, head, (ws: WebSocket, msg: IncomingMessage) => { - wss.emit('connection', ws, msg); - }); -} - -/** The http web server. */ -const server = createServer(); -/** The websocket server to handle websocket requests. */ -const wss = new WebSocketServer({noServer: true}); - -wss.on('connection', wsHandler); -server.on('upgrade', upgradeHandler); -server.on('listening', () => console.log('Credential Service startup complete, listening')); -server.listen(PORT); diff --git a/apps/credential-service/package.json b/apps/credential-service/package.json deleted file mode 100644 index 3dbc1ca59..000000000 --- a/apps/credential-service/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "type": "module" -} diff --git a/apps/functions/BUILD.bazel b/apps/functions/BUILD.bazel index 85dae4ac3..b2fb2eb5d 100644 --- a/apps/functions/BUILD.bazel +++ b/apps/functions/BUILD.bazel @@ -1,5 +1,5 @@ -load("//tools:defaults.bzl", "esbuild_cjs_bundle", "ts_library") load("@build_bazel_rules_nodejs//:index.bzl", "copy_to_bin") +load("//tools:defaults.bzl", "esbuild_cjs_bundle", "ts_library") package(default_visibility = ["//visibility:private"]) @@ -19,8 +19,6 @@ ts_library( deps = [ "//apps/functions/code-of-conduct", "//apps/functions/dns-redirecting", - "//apps/functions/githubWebhook", - "//apps/functions/ng-dev", "@npm//firebase-admin", ], ) @@ -30,10 +28,6 @@ esbuild_cjs_bundle( entry_points = [ "index.ts", ], - external = [ - # Mark farmhash as external as it loads .node files directly, which are unused in our usage - "farmhash", - ], visibility = ["//apps:__pkg__"], deps = [ ":functions", diff --git a/apps/functions/code-of-conduct/blockUser.ts b/apps/functions/code-of-conduct/blockUser.ts index 9d2d25c57..0d9130ca4 100644 --- a/apps/functions/code-of-conduct/blockUser.ts +++ b/apps/functions/code-of-conduct/blockUser.ts @@ -31,13 +31,15 @@ export const blockUser = functions.https.onCall( throw Error(); } - await github.orgs.blockUser({org: 'angular', username: username}).catch((err: RequestError) => { - // If a user is already blocked, we can continue silently failing as action still "succeeded". - if (err.message === 'Blocked user has already been blocked' && err.status === 422) { - return; - } - throw err; - }); + await github + .request('PUT /orgs/{org}/blocks/{username}', {org: 'angular', username: username}) + .catch((err: RequestError) => { + // If a user is already blocked, we can continue silently failing as action still "succeeded". + if (err.message === 'Blocked user has already been blocked' && err.status === 422) { + return; + } + throw err; + }); await userDoc.ref.create({ comments: comments, diff --git a/apps/functions/githubWebhook/BUILD.bazel b/apps/functions/githubWebhook/BUILD.bazel deleted file mode 100644 index 5f18d55f0..000000000 --- a/apps/functions/githubWebhook/BUILD.bazel +++ /dev/null @@ -1,33 +0,0 @@ -load("//tools:defaults.bzl", "ts_library") - -package(default_visibility = ["//visibility:private"]) - -ts_library( - name = "githubWebhook", - srcs = [ - "index.ts", - ], - visibility = [ - "//apps/functions:__pkg__", - ], - deps = [ - ":webhooks", - "@npm//@octokit/webhooks-types", - "@npm//firebase-admin", - "@npm//firebase-functions", - ], -) - -ts_library( - name = "webhooks", - srcs = [ - "label.ts", - "pull-request.ts", - "status.ts", - ], - deps = [ - "//apps/shared/models:server", - "@npm//@octokit/webhooks-types", - "@npm//firebase-admin", - ], -) diff --git a/apps/functions/githubWebhook/index.ts b/apps/functions/githubWebhook/index.ts deleted file mode 100644 index 007431896..000000000 --- a/apps/functions/githubWebhook/index.ts +++ /dev/null @@ -1,55 +0,0 @@ -import * as functions from 'firebase-functions'; -import {handlePullRequestEvent} from './pull-request.js'; -import {handleStatusEvent} from './status.js'; -import {LabelEvent, StatusEvent} from '@octokit/webhooks-types'; -import {handleLabelEvent} from './label.js'; - -/** - * Firebase function to handle all incoming webhooks from Github. This function checks the incoming - * webhook to ensure it is a valid request from Github, and then delegates processing of a payload - * to one of the other githubWebhook functions. - */ -export const githubWebhook = functions.https.onRequest( - {invoker: 'public', timeoutSeconds: 30, minInstances: 1}, - async (request, response) => { - if (request.method !== 'POST') { - response.status(405); - response.send('Requests must be made using the POST method.'); - return; - } - if (request.headers['content-type'] !== 'application/json') { - response.status(415); - response.send('Request payloads must be "application/json".'); - return; - } - - if (request.get('X-GitHub-Event') === 'pull_request') { - await handlePullRequestEvent(request.body); - response.send(`Handled pull request update for pr #${request.body.pull_request.number}`); - response.end(); - return; - } - - if (request.get('X-GitHub-Event') === 'status') { - const statusEvent = request.body as StatusEvent; - await handleStatusEvent(statusEvent); - response.send( - `Handled status update for status for commit ${statusEvent.sha} with the context ${statusEvent.context}`, - ); - response.end(); - return; - } - - if (request.get('X-GitHub-Event') === 'label') { - const labelEvent = request.body as LabelEvent; - await handleLabelEvent(labelEvent); - response.send( - `Handled label ${labelEvent.action} for '${labelEvent.label.name} in ${labelEvent.repository.full_name}`, - ); - response.end(); - return; - } - - response.end(); - }, -); diff --git a/apps/functions/githubWebhook/label.ts b/apps/functions/githubWebhook/label.ts deleted file mode 100644 index e708f2ee8..000000000 --- a/apps/functions/githubWebhook/label.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {LabelEvent} from '@octokit/webhooks-types'; -import {firestore} from 'firebase-admin'; -// TODO(josephperrott): Remove usage of FirestoreReference and fromFirestoreReference. -import { - Label, - FirestoreReference, - fromFirestoreReference, -} from '../../shared/models/server-models.js'; - -export async function handleLabelEvent(event: LabelEvent) { - const {getFirestoreRefForGithubModel, fromGithub} = Label.getGithubHelpers(); - /** The label model for the label data. */ - const label = fromGithub(event.label); - /** FirestoreReference for the label. */ - const firestoreRef = getFirestoreRefForGithubModel(event) as FirestoreReference