-
Notifications
You must be signed in to change notification settings - Fork 219
feat(auth): Use libs/email sender in fxa-auth-server #19844
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
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
140 changes: 140 additions & 0 deletions
140
libs/accounts/email-renderer/src/renderer/email-helpers.spec.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| /* 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 { | ||
| constructLocalDateString, | ||
| constructLocalTimeAndDateStrings, | ||
| } from './email-helpers'; | ||
|
|
||
| describe('EmailHelpers', () => { | ||
| afterEach(() => { | ||
| jest.restoreAllMocks(); | ||
| }); | ||
|
|
||
| describe('constructLocalTimeAndDateStrings', () => { | ||
| beforeEach(() => { | ||
| jest | ||
| .spyOn(Date, 'now') | ||
| .mockReturnValue(new Date('2024-01-15T20:30:45Z').getTime()); | ||
| }); | ||
|
|
||
| it('should construct time and date strings with timezone', () => { | ||
| const result = constructLocalTimeAndDateStrings( | ||
| 'America/Los_Angeles', | ||
| 'en-US' | ||
| ); | ||
|
|
||
| expect(result.time).toEqual('12:30:45 PM (PST)'); | ||
| expect(result.date).toEqual('Monday, Jan 15, 2024'); | ||
| }); | ||
|
|
||
| it('should use default timezone when not provided', () => { | ||
| const result = constructLocalTimeAndDateStrings(undefined, 'en-US'); | ||
|
|
||
| expect(result.time).toEqual('8:30:45 PM (UTC)'); | ||
| expect(result.date).toEqual('Monday, Jan 15, 2024'); | ||
| expect(result.acceptLanguage).toEqual('en-US'); | ||
| expect(result.timeZone).toEqual('Etc/UTC'); | ||
| }); | ||
|
|
||
| it('should use default locale when not provided', () => { | ||
| const result = constructLocalTimeAndDateStrings('America/New_York'); | ||
|
|
||
| expect(result.time).toEqual('3:30:45 PM (EST)'); | ||
| expect(result.date).toEqual('Monday, Jan 15, 2024'); | ||
| expect(result.acceptLanguage).toEqual('en'); | ||
| expect(result.timeZone).toEqual('America/New_York'); | ||
| }); | ||
|
|
||
| it('should handle different locales', () => { | ||
| const result = constructLocalTimeAndDateStrings('Europe/London', 'fr-FR'); | ||
|
|
||
| expect(result.time).toEqual('20:30:45 (GMT)'); | ||
| expect(result.date).toEqual('lundi, 15 janv. 2024'); | ||
| }); | ||
|
|
||
| it('should format with no parameters', () => { | ||
| const result = constructLocalTimeAndDateStrings(); | ||
|
|
||
| expect(result.time).toEqual('8:30:45 PM (UTC)'); | ||
| expect(result.date).toEqual('Monday, Jan 15, 2024'); | ||
| }); | ||
| }); | ||
|
|
||
| describe('constructLocalDateString', () => { | ||
| it('should construct localized date string with timezone', () => { | ||
| const date = new Date('2024-01-15T12:00:00Z'); | ||
|
|
||
| const result = constructLocalDateString( | ||
| 'America/Los_Angeles', | ||
| 'en-US', | ||
| date | ||
| ); | ||
|
|
||
| expect(result).toEqual('01/15/2024'); | ||
| }); | ||
|
|
||
| it('should accept custom format string', () => { | ||
| const date = new Date('2024-01-15T12:00:00Z'); | ||
|
|
||
| const result = constructLocalDateString( | ||
| 'America/Chicago', | ||
| 'en-US', | ||
| date, | ||
| 'YYYY-MM-DD' | ||
| ); | ||
|
|
||
| expect(result).toEqual('2024-01-15'); | ||
| }); | ||
|
|
||
| it('should use current date when not provided', () => { | ||
| jest | ||
| .spyOn(Date, 'now') | ||
| .mockReturnValue(new Date('2024-01-15T12:00:00Z').getTime()); | ||
|
|
||
| const result = constructLocalDateString('America/Denver', 'en-US'); | ||
|
|
||
| expect(result).toEqual('01/15/2024'); | ||
| }); | ||
|
|
||
| it('should handle different locales', () => { | ||
| const date = new Date('2024-01-15T12:00:00Z'); | ||
|
|
||
| const result = constructLocalDateString('Europe/Paris', 'de-DE', date); | ||
|
|
||
| expect(result).toEqual('15.01.2024'); | ||
| }); | ||
|
|
||
| it('should accept timestamp as date parameter', () => { | ||
| jest | ||
| .spyOn(Date, 'now') | ||
| .mockReturnValue(new Date('2025-12-31T23:59:59Z').getTime()); | ||
|
|
||
| const timestamp = Date.now(); | ||
|
|
||
| const result = constructLocalDateString( | ||
| 'Asia/Tokyo', | ||
| 'ja-JP', | ||
| timestamp // epoch timestamp instead of Date object | ||
| ); | ||
|
|
||
| expect(result).toEqual('2026/01/01'); | ||
| }); | ||
|
|
||
| it('should use default timezone when not provided', () => { | ||
| const date = new Date('2024-01-15T02:00:00Z'); | ||
| const resultDefault = constructLocalDateString(undefined, 'en-US', date); | ||
|
|
||
| const resultEst = constructLocalDateString( | ||
| 'America/New_York', | ||
| 'en-US', | ||
| date | ||
| ); | ||
|
|
||
| expect(resultDefault).not.toBe(resultEst); | ||
| expect(resultDefault).toEqual('01/15/2024'); | ||
| expect(resultEst).toEqual('01/14/2024'); | ||
| }); | ||
| }); | ||
| }); |
101 changes: 101 additions & 0 deletions
101
libs/accounts/email-renderer/src/renderer/email-helpers.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| /* 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 moment from 'moment-timezone'; | ||
| import { determineLocale } from '@fxa/shared/l10n'; | ||
|
|
||
| const DEFAULT_LOCALE = 'en'; | ||
| const DEFAULT_TIMEZONE = 'Etc/UTC'; | ||
|
|
||
| /** | ||
| * Takes a list of emails, returning the primary email as "to" and | ||
| * verified, non-primary emails as "cc". | ||
| * @param emails | ||
| * @returns | ||
| */ | ||
| export const splitEmails = ( | ||
| emails: { email: string; isPrimary?: boolean; isVerified?: boolean }[] | ||
| ): { to: string; cc: string[] } => { | ||
| return emails.reduce( | ||
| (result: { to: string; cc: string[] }, item) => { | ||
| const { email } = item; | ||
|
|
||
| if (item.isPrimary) { | ||
| result.to = email; | ||
| } else if (item.isVerified) { | ||
| result.cc.push(email); | ||
| } | ||
|
|
||
| return result; | ||
| }, | ||
| { to: '', cc: [] } | ||
| ); | ||
| }; | ||
|
|
||
| /** | ||
| * Construct a localized time string with timezone. | ||
| * Returns an object containing the localized time and date strings, | ||
| * as well as the acceptLanguage and timeZone used to generate them. | ||
| * | ||
| * Example output: ['9:41:00 AM (PDT)', 'Monday, Jan 1, 2024'] | ||
| * | ||
| * @param timeZone - IANA timezone (e.g., 'America/Los_Angeles') | ||
| * @param acceptLanguage - Accept-Language header value | ||
| */ | ||
| export const constructLocalTimeAndDateStrings = ( | ||
| timeZone?: string, | ||
| acceptLanguage?: string | ||
| ): { | ||
| acceptLanguage: string; | ||
| date: string; | ||
| time: string; | ||
| timeZone: string; | ||
| } => { | ||
| moment.tz.setDefault(DEFAULT_TIMEZONE); | ||
|
|
||
| const locale = determineLocale(acceptLanguage) || DEFAULT_LOCALE; | ||
| moment.locale(locale); | ||
|
|
||
| let timeMoment = moment(); | ||
| if (timeZone) { | ||
| timeMoment = timeMoment.tz(timeZone); | ||
| } | ||
|
|
||
| const time = timeMoment.format('LTS (z)'); | ||
| const date = timeMoment.format('dddd, ll'); | ||
|
|
||
| return { | ||
| acceptLanguage: locale, | ||
| date, | ||
| time, | ||
| timeZone: timeZone || DEFAULT_TIMEZONE, | ||
| }; | ||
| }; | ||
|
|
||
| /** | ||
| * Construct a localized date string. | ||
| * | ||
| * @param timeZone - IANA timezone (e.g., 'America/Los_Angeles') | ||
| * @param acceptLanguage - Accept-Language header value | ||
| * @param date - Date to format (defaults to now) | ||
| * @param formatString - Moment.js format string (defaults to 'L' for localized date) | ||
| */ | ||
| export const constructLocalDateString = ( | ||
| timeZone?: string, | ||
| acceptLanguage?: string, | ||
| date?: Date | number, | ||
| formatString = 'L' | ||
| ): string => { | ||
| moment.tz.setDefault(DEFAULT_TIMEZONE); | ||
|
|
||
| const locale = determineLocale(acceptLanguage) || DEFAULT_LOCALE; | ||
| moment.locale(locale); | ||
|
|
||
| let time = moment(date); | ||
| if (timeZone) { | ||
| time = time.tz(timeZone); | ||
| } | ||
|
|
||
| return time.format(formatString); | ||
| }; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.