-
Notifications
You must be signed in to change notification settings - Fork 223
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
MNTOR-3435 - Convert Breaches.js to Typescript #4876
Changes from 9 commits
e38098a
6122347
056ce4b
5f8a670
d91e107
34a4a60
114673b
b0826a3
aa84eb3
104824e
87f98de
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
import { getUserEmails } from "../db/tables/emailAddresses.js"; | ||
import { | ||
getBreachesForEmail, | ||
getFilteredBreaches, | ||
HibpLikeDbBreach, | ||
} from "./hibp"; | ||
import { getSha1 } from "./fxa"; | ||
import { captureMessage } from "@sentry/node"; | ||
import { EmailAddressRow, SubscriberRow } from "knex/types/tables"; | ||
|
||
export type BundledVerifiedEmails = { | ||
email: string; | ||
breaches: HibpLikeDbBreach[]; | ||
id: number; | ||
primary: boolean; | ||
verified: boolean; | ||
hasNewBreaches?: number; | ||
}; | ||
|
||
export type AllEmailsAndBreaches = { | ||
unverifiedEmails: EmailAddressRow[]; | ||
verifiedEmails: BundledVerifiedEmails[]; | ||
}; | ||
|
||
type userType = | ||
| ({ | ||
email_addresses: Array<{ | ||
id: EmailAddressRow["id"]; | ||
email: EmailAddressRow["email"]; | ||
}>; | ||
} & SubscriberRow) | ||
| undefined; | ||
|
||
async function getAllEmailsAndBreaches( | ||
user: userType, | ||
allBreaches: HibpLikeDbBreach[], | ||
): Promise<AllEmailsAndBreaches> { | ||
// @ts-ignore: function will be deprecated | ||
const verifiedEmails: BundledVerifiedEmails[] = []; | ||
// @ts-ignore: function will be deprecated | ||
const unverifiedEmails: EmailAddressRow[] = []; | ||
|
||
if (!user) { | ||
const errMsg = "getAllEmailsAndBreaches: subscriber cannot be undefined"; | ||
console.error(errMsg); | ||
captureMessage(errMsg); | ||
|
||
// @ts-ignore: function will be deprecated | ||
return { verifiedEmails, unverifiedEmails }; | ||
} | ||
if (!allBreaches || allBreaches.length === 0) { | ||
const errMsg = | ||
"getAllEmailsAndBreaches: allBreaches object cannot be empty"; | ||
console.error(errMsg); | ||
captureMessage(errMsg); | ||
// @ts-ignore: function will be deprecated | ||
return { verifiedEmails, unverifiedEmails }; | ||
} | ||
|
||
const monitoredEmails = await getUserEmails(user.id); | ||
verifiedEmails.push( | ||
await bundleVerifiedEmails({ | ||
user, | ||
email: user.primary_email, | ||
recordId: user.id, | ||
recordVerified: user.primary_verified, | ||
allBreaches, | ||
}), | ||
); | ||
for (const email of monitoredEmails) { | ||
if (email.verified) { | ||
verifiedEmails.push( | ||
await bundleVerifiedEmails({ | ||
user, | ||
email: user.primary_email, | ||
recordId: email.id, | ||
recordVerified: email.verified, | ||
allBreaches, | ||
}), | ||
); | ||
} else { | ||
unverifiedEmails.push(email); | ||
} | ||
} | ||
|
||
// get new breaches since last shown | ||
for (const emailEntry of verifiedEmails) { | ||
// /** @type {any[]} */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this comment be removed now? |
||
const newBreachesForEmail = emailEntry.breaches.filter( | ||
(breach) => breach.AddedDate >= user.breaches_last_shown, | ||
); | ||
|
||
for (const newBreachForEmail of newBreachesForEmail) { | ||
newBreachForEmail.NewBreach = true; // add "NewBreach" property to the new breach. | ||
emailEntry.hasNewBreaches = newBreachesForEmail.length; // add the number of new breaches to the email | ||
} | ||
} | ||
|
||
return { verifiedEmails, unverifiedEmails }; | ||
} | ||
|
||
function addRecencyIndex(foundBreaches: HibpLikeDbBreach[]) { | ||
// /** | ||
// * @type {any[]} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question re: this type definition - we were using these JSDoc type annotations to get a little type checking for JS files before we fully switched to typescript but we shouldn't need them anymore. |
||
// */ | ||
const annotatedBreaches: HibpLikeDbBreach[] = []; | ||
// slice() the array to make a copy so before reversing so we don't | ||
// reverse foundBreaches in-place | ||
const oldestToNewestFoundBreaches = foundBreaches.slice().reverse(); | ||
oldestToNewestFoundBreaches.forEach((annotatingBreach, index) => { | ||
const foundBreach = foundBreaches.find( | ||
(foundBreach) => foundBreach.Name === annotatingBreach.Name, | ||
); | ||
annotatedBreaches.push(Object.assign({ recencyIndex: index }, foundBreach)); | ||
}); | ||
return annotatedBreaches.reverse(); | ||
} | ||
|
||
type options = { | ||
user: userType; | ||
email: string; | ||
recordId: number; | ||
recordVerified: boolean; | ||
allBreaches: HibpLikeDbBreach[]; | ||
}; | ||
async function bundleVerifiedEmails( | ||
options: options, | ||
): Promise<BundledVerifiedEmails> { | ||
const { user, email, recordId, recordVerified, allBreaches } = options; | ||
const lowerCaseEmailSha = getSha1(email.toLowerCase()); | ||
|
||
// find all breaches relevant to the current email | ||
const foundBreaches = await getBreachesForEmail( | ||
lowerCaseEmailSha, | ||
allBreaches, | ||
true, | ||
false, | ||
); | ||
|
||
// TODO: remove after migration MNTOR-978 | ||
// adding index to breaches based on recency | ||
const foundBreachesWithRecency = addRecencyIndex(foundBreaches); | ||
|
||
if (!user) { | ||
const errMsg = "breachResolutionV2: subscriber cannot be undefined"; | ||
console.error(errMsg); | ||
captureMessage(errMsg); | ||
|
||
// @ts-ignore: function will be deprecated | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you put There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Heads-up that that needs to be in a JSDoc comment, i.e. one that starts with |
||
return { verifiedEmails, unverifiedEmails }; | ||
} | ||
|
||
// filter out irrelevant breaches based on HIBP | ||
const filteredAnnotatedFoundBreaches = getFilteredBreaches( | ||
foundBreachesWithRecency, | ||
); | ||
|
||
const emailEntry: BundledVerifiedEmails = { | ||
email: email, | ||
breaches: filteredAnnotatedFoundBreaches, | ||
primary: email === user.primary_email, | ||
id: recordId, | ||
verified: recordVerified, | ||
}; | ||
|
||
return emailEntry; | ||
} | ||
|
||
export { getAllEmailsAndBreaches }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Vinnl I had to rebase this code, could you check that it did not affect the changes in the
hibp.js
typescript migration?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added ec002a1 to restore a couple of changes that were resolved the wrong way around.
FWIW, if I have a merge conflict after rebasing from
main
, I simply look at the code that is inmain
- if the conflict is different frommain
and is not a change that I intentionally made as part of the PR, I restore it back to what's currently inmain
.