diff --git a/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.test.ts b/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.test.ts index fa60a74139e..7ec9120a6a8 100644 --- a/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.test.ts +++ b/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.test.ts @@ -1,6 +1,5 @@ import { Messenger } from '@metamask/base-controller'; import type { AuthenticationController } from '@metamask/profile-sync-controller'; -import log from 'loglevel'; import NotificationServicesPushController from './NotificationServicesPushController'; import type { @@ -11,10 +10,6 @@ import type { import * as services from './services/services'; import type { PushNotificationEnv } from './types'; -// Testing util to clean up verbose logs when testing errors -const mockErrorLog = () => - jest.spyOn(log, 'error').mockImplementation(jest.fn()); - const MOCK_JWT = 'mockJwt'; const MOCK_FCM_TOKEN = 'mockFcmToken'; const MOCK_MOBILE_FCM_TOKEN = 'mockMobileFcmToken'; @@ -89,21 +84,9 @@ describe('NotificationServicesPushController', () => { arrangeServicesMocks(); const { controller, messenger } = arrangeMockMessenger(); mockAuthBearerTokenCall(messenger); - await controller.disablePushNotifications(MOCK_TRIGGERS); + await controller.disablePushNotifications(); expect(controller.state.fcmToken).toBe(''); }); - - it('should fail if a jwt token is not provided', async () => { - arrangeServicesMocks(); - mockErrorLog(); - const { controller, messenger } = arrangeMockMessenger(); - mockAuthBearerTokenCall(messenger).mockResolvedValue( - null as unknown as string, - ); - await expect(controller.disablePushNotifications([])).rejects.toThrow( - expect.any(Error), - ); - }); }); describe('updateTriggerPushNotifications', () => { @@ -127,7 +110,6 @@ describe('NotificationServicesPushController', () => { const args = spy.mock.calls[0][0]; expect(args.bearerToken).toBe(MOCK_JWT); expect(args.triggers).toBe(MOCK_TRIGGERS); - expect(args.regToken).toBe(controller.state.fcmToken); }); }); }); diff --git a/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.ts b/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.ts index daf07e6c64b..4fa94e31ae7 100644 --- a/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.ts +++ b/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.ts @@ -286,26 +286,18 @@ export default class NotificationServicesPushController extends BaseController< /** * Disables push notifications for the application. - * This method handles the process of disabling push notifications by: - * 1. Unregistering the service worker to stop listening for messages. - * 2. Sending a request to the server to unregister the device using the FCM token. - * 3. Removing the FCM token from the state to complete the process. - * - * @param UUIDs - An array of UUIDs for which push notifications should be disabled. + * This removes the registration token on this device, and ensures we unsubscribe from any listeners */ - async disablePushNotifications(UUIDs: string[]) { + async disablePushNotifications() { if (!this.#config.isPushEnabled) { return; } - const bearerToken = await this.#getAndAssertBearerToken(); let isPushNotificationsDisabled: boolean; try { // Send a request to the server to unregister the token/device isPushNotificationsDisabled = await deactivatePushNotifications({ - bearerToken, - triggers: UUIDs, env: this.#env, deleteRegToken, regToken: this.state.fcmToken, @@ -356,7 +348,6 @@ export default class NotificationServicesPushController extends BaseController< createRegToken, deleteRegToken, platform: this.#config.platform, - regToken: this.state.fcmToken, }); // update the state with the new FCM token diff --git a/packages/notification-services-controller/src/NotificationServicesPushController/__fixtures__/mockServices.ts b/packages/notification-services-controller/src/NotificationServicesPushController/__fixtures__/mockServices.ts index 004dcb69ea0..18d709729b9 100644 --- a/packages/notification-services-controller/src/NotificationServicesPushController/__fixtures__/mockServices.ts +++ b/packages/notification-services-controller/src/NotificationServicesPushController/__fixtures__/mockServices.ts @@ -1,29 +1,12 @@ import nock from 'nock'; -import { - getMockRetrievePushNotificationLinksResponse, - getMockUpdatePushNotificationLinksResponse, -} from './mockResponse'; +import { getMockUpdatePushNotificationLinksResponse } from './mockResponse'; type MockReply = { status: nock.StatusCode; body?: nock.Body; }; -export const mockEndpointGetPushNotificationLinks = (mockReply?: MockReply) => { - const mockResponse = getMockRetrievePushNotificationLinksResponse(); - const reply = mockReply ?? { - status: 200, - body: mockResponse.response, - }; - - const mockEndpoint = nock(mockResponse.url) - .get('') - .reply(reply.status, reply.body); - - return mockEndpoint; -}; - export const mockEndpointUpdatePushNotificationLinks = ( mockReply?: MockReply, ) => { diff --git a/packages/notification-services-controller/src/NotificationServicesPushController/services/services.test.ts b/packages/notification-services-controller/src/NotificationServicesPushController/services/services.test.ts index 9ae15d1b679..d33017b647f 100644 --- a/packages/notification-services-controller/src/NotificationServicesPushController/services/services.test.ts +++ b/packages/notification-services-controller/src/NotificationServicesPushController/services/services.test.ts @@ -4,15 +4,11 @@ import * as PushWebModule from './push/push-web'; import { activatePushNotifications, deactivatePushNotifications, - getPushNotificationLinks, listenToPushNotifications, updateLinksAPI, updateTriggerPushNotifications, } from './services'; -import { - mockEndpointGetPushNotificationLinks, - mockEndpointUpdatePushNotificationLinks, -} from '../__fixtures__/mockServices'; +import { mockEndpointUpdatePushNotificationLinks } from '../__fixtures__/mockServices'; import type { PushNotificationEnv } from '../types/firebase'; // Testing util to clean up verbose logs when testing errors @@ -26,24 +22,6 @@ const MOCK_TRIGGERS = ['1', '2', '3']; const MOCK_JWT = 'MOCK_JWT'; describe('NotificationServicesPushController Services', () => { - describe('getPushNotificationLinks', () => { - it('should return reg token links', async () => { - const mockAPI = mockEndpointGetPushNotificationLinks(); - const result = await getPushNotificationLinks(MOCK_JWT); - expect(mockAPI.isDone()).toBe(true); - expect(result?.registration_tokens).toBeDefined(); - expect(result?.trigger_ids).toBeDefined(); - }); - - it('should return null if given a bad response', async () => { - const mockAPI = mockEndpointGetPushNotificationLinks({ status: 500 }); - mockErrorLog(); - const result = await getPushNotificationLinks(MOCK_JWT); - expect(mockAPI.isDone()).toBe(true); - expect(result).toBeNull(); - }); - }); - describe('updateLinksAPI', () => { const act = async () => await updateLinksAPI(MOCK_JWT, MOCK_TRIGGERS, [ @@ -74,10 +52,7 @@ describe('NotificationServicesPushController Services', () => { }); describe('activatePushNotifications', () => { - const arrangeMocks = (override?: { - mockGet?: { status: number }; - mockPut?: { status: number }; - }) => { + const arrangeMocks = (override?: { mockPut?: { status: number } }) => { const params = { bearerToken: MOCK_JWT, triggers: MOCK_TRIGGERS, @@ -96,7 +71,6 @@ describe('NotificationServicesPushController Services', () => { params, mobileParams, apis: { - mockGet: mockEndpointGetPushNotificationLinks(override?.mockGet), mockPut: mockEndpointUpdatePushNotificationLinks(override?.mockPut), }, }; @@ -106,7 +80,6 @@ describe('NotificationServicesPushController Services', () => { const { params, apis } = arrangeMocks(); const result = await activatePushNotifications(params); - expect(apis.mockGet.isDone()).toBe(true); expect(params.createRegToken).toHaveBeenCalled(); expect(apis.mockPut.isDone()).toBe(true); @@ -118,32 +91,18 @@ describe('NotificationServicesPushController Services', () => { mockErrorLog(); const result = await activatePushNotifications(mobileParams); - expect(apis.mockGet.isDone()).toBe(true); expect(mobileParams.createRegToken).not.toHaveBeenCalled(); expect(apis.mockPut.isDone()).toBe(true); expect(result).toBe(MOCK_MOBILE_FCM_TOKEN); }); - it('should return null if unable to get links from API', async () => { - const { params, apis } = arrangeMocks({ mockGet: { status: 500 } }); - mockErrorLog(); - const result = await activatePushNotifications(params); - - expect(apis.mockGet.isDone()).toBe(true); - expect(params.createRegToken).not.toHaveBeenCalled(); - expect(apis.mockPut.isDone()).toBe(false); - - expect(result).toBeNull(); - }); - it('should return null if unable to create new registration token', async () => { const { params, apis } = arrangeMocks(); params.createRegToken.mockRejectedValue(new Error('MOCK ERROR')); const result = await activatePushNotifications(params); - expect(apis.mockGet.isDone()).toBe(true); expect(params.createRegToken).toHaveBeenCalled(); expect(apis.mockPut.isDone()).toBe(false); @@ -152,10 +111,7 @@ describe('NotificationServicesPushController Services', () => { }); describe('deactivatePushNotifications', () => { - const arrangeMocks = (override?: { - mockGet?: { status: number }; - mockPut?: { status: number }; - }) => { + const arrangeMocks = () => { const params = { regToken: MOCK_REG_TOKEN, bearerToken: MOCK_JWT, @@ -166,80 +122,41 @@ describe('NotificationServicesPushController Services', () => { return { params, - apis: { - mockGet: mockEndpointGetPushNotificationLinks(override?.mockGet), - mockPut: mockEndpointUpdatePushNotificationLinks(override?.mockPut), - }, }; }; it('should successfully delete the registration token', async () => { - const { params, apis } = arrangeMocks(); + const { params } = arrangeMocks(); const result = await deactivatePushNotifications(params); - expect(apis.mockGet.isDone()).toBe(true); - expect(apis.mockPut.isDone()).toBe(true); expect(params.deleteRegToken).toHaveBeenCalled(); - expect(result).toBe(true); }); it('should return early when there is no registration token to delete', async () => { - const { params, apis } = arrangeMocks(); + const { params } = arrangeMocks(); mockErrorLog(); const result = await deactivatePushNotifications({ ...params, regToken: '', }); - expect(apis.mockGet.isDone()).toBe(false); - expect(apis.mockPut.isDone()).toBe(false); expect(params.deleteRegToken).not.toHaveBeenCalled(); - expect(result).toBe(true); }); - it('should return false when unable to get links api', async () => { - const { params, apis } = arrangeMocks({ mockGet: { status: 500 } }); - mockErrorLog(); - const result = await deactivatePushNotifications(params); - - expect(apis.mockGet.isDone()).toBe(true); - expect(apis.mockPut.isDone()).toBe(false); - expect(params.deleteRegToken).not.toHaveBeenCalled(); - - expect(result).toBe(false); - }); - - it('should return false when unable to update links api', async () => { - const { params, apis } = arrangeMocks({ mockPut: { status: 500 } }); - const result = await deactivatePushNotifications(params); - - expect(apis.mockGet.isDone()).toBe(true); - expect(apis.mockPut.isDone()).toBe(true); - expect(params.deleteRegToken).not.toHaveBeenCalled(); - - expect(result).toBe(false); - }); - it('should return false when unable to delete the existing reg token', async () => { - const { params, apis } = arrangeMocks(); + const { params } = arrangeMocks(); params.deleteRegToken.mockResolvedValue(false); const result = await deactivatePushNotifications(params); - expect(apis.mockGet.isDone()).toBe(true); - expect(apis.mockPut.isDone()).toBe(true); expect(params.deleteRegToken).toHaveBeenCalled(); - expect(result).toBe(false); }); }); describe('updateTriggerPushNotifications', () => { - const arrangeMocks = (override?: { - mockGet?: { status: number }; - mockPut?: { status: number }; - }) => { + const arrangeMocks = (override?: { mockPut?: { status: number } }) => { const params = { regToken: MOCK_REG_TOKEN, bearerToken: MOCK_JWT, @@ -253,7 +170,6 @@ describe('NotificationServicesPushController Services', () => { return { params, apis: { - mockGet: mockEndpointGetPushNotificationLinks(override?.mockGet), mockPut: mockEndpointUpdatePushNotificationLinks(override?.mockPut), }, }; @@ -264,7 +180,6 @@ describe('NotificationServicesPushController Services', () => { mockErrorLog(); const result = await updateTriggerPushNotifications(params); - expect(apis.mockGet.isDone()).toBe(true); expect(params.deleteRegToken).toHaveBeenCalled(); expect(params.createRegToken).toHaveBeenCalled(); expect(apis.mockPut.isDone()).toBe(true); @@ -273,20 +188,6 @@ describe('NotificationServicesPushController Services', () => { expect(result.isTriggersLinkedToPushNotifications).toBe(true); }); - it('should return early if fails to get links api', async () => { - const { params, apis } = arrangeMocks({ mockGet: { status: 500 } }); - mockErrorLog(); - const result = await updateTriggerPushNotifications(params); - - expect(apis.mockGet.isDone()).toBe(true); - expect(params.deleteRegToken).not.toHaveBeenCalled(); - expect(params.createRegToken).not.toHaveBeenCalled(); - expect(apis.mockPut.isDone()).toBe(false); - - expect(result.fcmToken).toBeUndefined(); - expect(result.isTriggersLinkedToPushNotifications).toBe(false); - }); - it('should throw error if fails to create reg token', async () => { const { params } = arrangeMocks(); params.createRegToken.mockResolvedValue(null); diff --git a/packages/notification-services-controller/src/NotificationServicesPushController/services/services.ts b/packages/notification-services-controller/src/NotificationServicesPushController/services/services.ts index 369a96f9d9f..59a7da2c3ab 100644 --- a/packages/notification-services-controller/src/NotificationServicesPushController/services/services.ts +++ b/packages/notification-services-controller/src/NotificationServicesPushController/services/services.ts @@ -1,5 +1,3 @@ -import log from 'loglevel'; - import * as endpoints from './endpoints'; import type { CreateRegToken, DeleteRegToken } from './push'; import { @@ -23,30 +21,6 @@ export type LinksResult = { registration_tokens: RegToken[]; }; -/** - * Fetches push notification links from a remote endpoint using a BearerToken for authorization. - * - * @param bearerToken - The JSON Web Token used for authorization. - * @returns A promise that resolves with the links result or null if an error occurs. - */ -export async function getPushNotificationLinks( - bearerToken: string, -): Promise { - try { - const response = await fetch(endpoints.REGISTRATION_TOKENS_ENDPOINT, { - headers: { Authorization: `Bearer ${bearerToken}` }, - }); - if (!response.ok) { - log.error('Failed to fetch the push notification links'); - throw new Error('Failed to fetch the push notification links'); - } - return response.json() as Promise; - } catch (error) { - log.error('Failed to fetch the push notification links', error); - return null; - } -} - /** * Updates the push notification links on a remote API. * @@ -103,29 +77,18 @@ export async function activatePushNotifications( const { bearerToken, triggers, env, createRegToken, platform, fcmToken } = params; - const notificationLinks = await getPushNotificationLinks(bearerToken); - - if (!notificationLinks) { - return null; - } - const regToken = fcmToken ?? (await createRegToken(env).catch(() => null)); if (!regToken) { return null; } - const newRegTokens = new Set(notificationLinks.registration_tokens); - newRegTokens.add({ token: regToken, platform }); - - await updateLinksAPI(bearerToken, triggers, Array.from(newRegTokens)); + await updateLinksAPI(bearerToken, triggers, [{ token: regToken, platform }]); return regToken; } type DeactivatePushNotificationsParams = { // Push Links regToken: string; - bearerToken: string; - triggers: string[]; // Push Un-registration env: PushNotificationEnv; @@ -133,39 +96,22 @@ type DeactivatePushNotificationsParams = { }; /** - * Disables push notifications by removing the registration token and unlinking triggers. + * Disables push notifications by removing the registration token + * We do not need to unlink triggers, and remove old reg tokens (this is cleaned up in the back-end) * * @param params - Deactivate Push Params - * @returns A promise that resolves with true if notifications were successfully disabled, false otherwise. + * @returns A promise that resolves with true if push notifications were successfully disabled, false otherwise. */ export async function deactivatePushNotifications( params: DeactivatePushNotificationsParams, ): Promise { - const { regToken, bearerToken, triggers, env, deleteRegToken } = params; + const { regToken, env, deleteRegToken } = params; // if we don't have a reg token, then we can early return if (!regToken) { return true; } - const notificationLinks = await getPushNotificationLinks(bearerToken); - if (!notificationLinks) { - return false; - } - - const filteredRegTokens = notificationLinks.registration_tokens.filter( - (r) => r.token !== regToken, - ); - - const isTokenRemovedFromAPI = await updateLinksAPI( - bearerToken, - triggers, - filteredRegTokens, - ); - if (!isTokenRemovedFromAPI) { - return false; - } - const isTokenRemovedFromFCM = await deleteRegToken(env); if (!isTokenRemovedFromFCM) { return false; @@ -176,7 +122,6 @@ export async function deactivatePushNotifications( type UpdateTriggerPushNotificationsParams = { // Push Links - regToken: string; bearerToken: string; triggers: string[]; @@ -207,7 +152,6 @@ export async function updateTriggerPushNotifications( }> { const { bearerToken, - regToken, triggers, createRegToken, platform, @@ -215,38 +159,21 @@ export async function updateTriggerPushNotifications( env, } = params; - const notificationLinks = await getPushNotificationLinks(bearerToken); - if (!notificationLinks) { - return { isTriggersLinkedToPushNotifications: false }; - } - // Create new registration token if doesn't exist - const hasRegToken = Boolean( - regToken && - notificationLinks.registration_tokens.some((r) => r.token === regToken), - ); - - let newRegToken: string | null = null; - if (!hasRegToken) { - await deleteRegToken(env); - newRegToken = await createRegToken(env); - if (!newRegToken) { - throw new Error('Failed to create a new registration token'); - } - notificationLinks.registration_tokens.push({ - token: newRegToken, - platform, - }); + await deleteRegToken(env); + const newRegToken = await createRegToken(env); + if (!newRegToken) { + throw new Error('Failed to create a new registration token'); } const isTriggersLinkedToPushNotifications = await updateLinksAPI( bearerToken, triggers, - notificationLinks.registration_tokens, + [{ token: newRegToken, platform }], ); return { isTriggersLinkedToPushNotifications, - fcmToken: newRegToken ?? null, + fcmToken: newRegToken, }; }