Skip to content

Commit

Permalink
chore(app-check): deprecation warning for v8 API ahead of future majo…
Browse files Browse the repository at this point in the history
…r release
  • Loading branch information
russellwheatley committed Dec 20, 2024
1 parent 10e618f commit 6b9ff91
Show file tree
Hide file tree
Showing 5 changed files with 243 additions and 15 deletions.
121 changes: 120 additions & 1 deletion packages/app-check/__tests__/appcheck.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { describe, expect, it } from '@jest/globals';
import { jest, beforeEach, describe, expect, it } from '@jest/globals';

// @ts-ignore test
import FirebaseModule from '../../app/lib/internal/FirebaseModule';

import {
createCheckV9Deprecation,
CheckV9DeprecationFunction,
} from '../../app/lib/common/unitTestUtils';

import {
firebase,
Expand All @@ -7,6 +15,8 @@ import {
getLimitedUseToken,
addTokenListener,
setTokenAutoRefreshEnabled,
onTokenChanged,
CustomProvider,
} from '../lib';

describe('appCheck()', function () {
Expand Down Expand Up @@ -54,5 +64,114 @@ describe('appCheck()', function () {
it('`setTokenAutoRefreshEnabled` function is properly exposed to end user', function () {
expect(setTokenAutoRefreshEnabled).toBeDefined();
});

it('`CustomProvider` function is properly exposed to end user', function () {
expect(CustomProvider).toBeDefined();
});
});

describe('test `console.warn` is called for RNFB v8 API & not called for v9 API', function () {
let appCheckRefV9Deprecation: CheckV9DeprecationFunction;
let staticsRefV9Deprecation: CheckV9DeprecationFunction;

beforeEach(function () {
appCheckRefV9Deprecation = createCheckV9Deprecation(['appCheck']);
staticsRefV9Deprecation = createCheckV9Deprecation(['appCheck', 'statics']);

// @ts-ignore test
jest.spyOn(FirebaseModule.prototype, 'native', 'get').mockImplementation(() => {
return new Proxy(
{},
{
get: () =>
jest.fn().mockResolvedValue({
source: 'cache',
changes: [],
documents: [],
metadata: {},
path: 'foo',
} as never),
},
);
});
});

describe('AppCheck', function () {
it('appCheck.activate()', function () {
const app = firebase.app();
const appCheck = firebase.appCheck();
appCheckRefV9Deprecation(
() =>
initializeAppCheck(app, {
provider: {
providerOptions: {
android: {
provider: 'playIntegrity',
},
},
},
isTokenAutoRefreshEnabled: true,
}),
() => appCheck.activate('string'),
'activate',
);
});

it('appCheck.setTokenAutoRefreshEnabled()', function () {
const appCheck = firebase.appCheck();
appCheckRefV9Deprecation(
() => setTokenAutoRefreshEnabled(appCheck, true),
() => appCheck.setTokenAutoRefreshEnabled(true),
'setTokenAutoRefreshEnabled',
);
});

it('appCheck.getToken()', function () {
const appCheck = firebase.appCheck();
appCheckRefV9Deprecation(
() => getToken(appCheck, true),
() => appCheck.getToken(true),
'getToken',
);
});

it('appCheck.getLimitedUseToken()', function () {
const appCheck = firebase.appCheck();
appCheckRefV9Deprecation(
() => getLimitedUseToken(appCheck),
() => appCheck.getLimitedUseToken(),
'getLimitedUseToken',
);
});

it('appCheck.onTokenChanged()', function () {
const appCheck = firebase.appCheck();
appCheckRefV9Deprecation(
() =>
onTokenChanged(
appCheck,
() => {},
() => {},
() => {},
),
() =>
appCheck.onTokenChanged(
() => {},
() => {},
() => {},
),
'onTokenChanged',
);
});

it('CustomProvider', function () {
const appCheck = firebase.appCheck;
staticsRefV9Deprecation(
() => CustomProvider,
() => appCheck.CustomProvider,
'CustomProvider',
);
});
});
});
});
47 changes: 47 additions & 0 deletions packages/app-check/lib/modular/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import AppCheckOptions = FirebaseAppCheckTypes.AppCheckOptions;
import AppCheckTokenResult = FirebaseAppCheckTypes.AppCheckTokenResult;
import PartialObserver = FirebaseAppCheckTypes.PartialObserver;
import Unsubscribe = FirebaseAppCheckTypes.Unsubscribe;
import AppCheckProvider = FirebaseAppCheckTypes.AppCheckProvider;
import CustomProviderOptions = FirebaseAppCheckTypes.CustomProviderOptions;

/**
* Activate App Check for the given app. Can be called only once per app.
Expand Down Expand Up @@ -52,6 +54,43 @@ export function addTokenListener(
listener: PartialObserver<AppCheckTokenResult>,
): Unsubscribe;

/**
* Registers a listener to changes in the token state. There can be more
* than one listener registered at the same time for one or more
* App Check instances. The listeners call back on the UI thread whenever
* the current token associated with this App Check instance changes.
*
* @returns A function that unsubscribes this listener.
*/
export function onTokenChanged(
appCheckInstance: AppCheck,
listener: PartialObserver<AppCheckTokenResult>,
): Unsubscribe;

/**
* Registers a listener to changes in the token state. There can be more
* than one listener registered at the same time for one or more
* App Check instances. The listeners call back on the UI thread whenever
* the current token associated with this App Check instance changes.
*
* Token listeners do not exist in the native SDK for iOS, no token change events will be emitted on that platform.
* This is not yet implemented on Android, no token change events will be emitted until implemented.
*
* NOTE: Although an `onError` callback can be provided, it will
* never be called, Android sdk code doesn't provide handling for onError function
*
* NOTE: Although an `onCompletion` callback can be provided, it will
* never be called because the token stream is never-ending.
*
* @returns A function that unsubscribes this listener.
*/
export function onTokenChanged(
appCheckInstance: AppCheck,
onNext: (tokenResult: AppCheckListenerResult) => void,
onError?: (error: Error) => void,
onCompletion?: () => void,
): () => void;

/**
* Set whether App Check will automatically refresh tokens as needed.
* @param appCheckInstance - AppCheck
Expand All @@ -61,3 +100,11 @@ export function setTokenAutoRefreshEnabled(
appCheckInstance: AppCheck,
isAutoRefreshEnabled: boolean,
): void;

/**
* Custom provider class.
* @public
*/
export class CustomProvider implements AppCheckProvider {
constructor(customProviderOptions: CustomProviderOptions);
}
45 changes: 39 additions & 6 deletions packages/app-check/lib/modular/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
*/

import { firebase } from '..';
import { MODULAR_DEPRECATION_ARG } from '../../../app/lib/common';
import CustomProvider from '../ReactNativeFirebaseAppCheckProvider';

/**
* @typedef {import('@firebase/app').FirebaseApp} FirebaseApp
Expand All @@ -35,11 +37,12 @@ import { firebase } from '..';
*/
export async function initializeAppCheck(app, options) {
if (app) {
await firebase.app(app.name).appCheck().initializeAppCheck(options);
const appCheck = firebase.app(app.name).appCheck();
await appCheck.initializeAppCheck.call(appCheck, options, MODULAR_DEPRECATION_ARG);
return { app: firebase.app(app.name) };
}

await firebase.app().appCheck().initializeAppCheck(options);
const appCheck = firebase.app().appCheck();
await appCheck.initializeAppCheck.call(appCheck, options, MODULAR_DEPRECATION_ARG);
return { app: firebase.app() };
}

Expand All @@ -51,7 +54,8 @@ export async function initializeAppCheck(app, options) {
* @returns {Promise<AppCheckTokenResult>}
*/
export function getToken(appCheckInstance, forceRefresh) {
return appCheckInstance.app.appCheck().getToken(forceRefresh);
const appCheck = appCheckInstance.app.appCheck();
return appCheck.getToken.call(appCheck, forceRefresh, MODULAR_DEPRECATION_ARG);
}

/**
Expand All @@ -61,7 +65,8 @@ export function getToken(appCheckInstance, forceRefresh) {
* @returns {Promise<AppCheckTokenResult>}
*/
export function getLimitedUseToken(appCheckInstance) {
return appCheckInstance.app.appCheck().getLimitedUseToken();
const appCheck = appCheckInstance.app.appCheck();
return appCheck.getLimitedUseToken.call(appCheck, MODULAR_DEPRECATION_ARG);
}

/**
Expand All @@ -85,5 +90,33 @@ export function addTokenListener(appCheckInstance, listener) {
* @param {boolean} isAutoRefreshEnabled - Whether to enable auto-refresh.
*/
export function setTokenAutoRefreshEnabled(appCheckInstance, isAutoRefreshEnabled) {
return appCheckInstance.app.appCheck().setTokenAutoRefreshEnabled(isAutoRefreshEnabled);
const appCheck = appCheckInstance.app.appCheck();
return appCheck.setTokenAutoRefreshEnabled.call(
appCheck,
isAutoRefreshEnabled,
MODULAR_DEPRECATION_ARG,
);
}

/**
* Registers a listener to changes in the token state. There can be more
* than one listener registered at the same time for one or more
* App Check instances. The listeners call back on the UI thread whenever
* the current token associated with this App Check instance changes.
*
* @param {AppCheck} appCheckInstance - The App Check instance.
* @param {PartialObserver<AppCheckTokenResult>} listener - The listener to register.
* @returns {Unsubscribe}
*/
export function onTokenChanged(appCheckInstance, onNextOrObserver, onError, onCompletion) {
const appCheck = appCheckInstance.app.appCheck();
return appCheck.onTokenChanged.call(
appCheck,
onNextOrObserver,
onError,
onCompletion,
MODULAR_DEPRECATION_ARG,
);
}

export { CustomProvider };
24 changes: 20 additions & 4 deletions packages/app/lib/common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,18 @@ export function tryJSONStringify(data) {
const NO_REPLACEMENT = true;

const mapOfDeprecationReplacements = {
appCheck: {
default: {
activate: 'initializeAppCheck()',
setTokenAutoRefreshEnabled: 'setTokenAutoRefreshEnabled()',
getToken: 'getToken()',
getLimitedUseToken: 'getLimitedUseToken()',
onTokenChanged: 'onTokenChanged()',
},
statics: {
CustomProvider: 'CustomProvider',
},
},
crashlytics: {
default: {
checkForUnsentReports: 'checkForUnsentReports()',
Expand Down Expand Up @@ -247,8 +259,8 @@ export function createMessage(
}

function getNamespace(target) {
if (target.GeoPoint) {
// target is statics object. GeoPoint is a static class on Firestore
if (target.GeoPoint || target.CustomProvider) {
// target is statics object. GeoPoint - Firestore, CustomProvider - AppCheck
return 'firestore';
}
if (target._config && target._config.namespace) {
Expand All @@ -263,8 +275,8 @@ function getNamespace(target) {
}

function getInstanceName(target) {
if (target.GeoPoint) {
// target is statics object. GeoPoint is a static class on Firestore
if (target.GeoPoint || target.CustomProvider) {
// target is statics object. GeoPoint - Firestore, CustomProvider - AppCheck
return 'statics';
}
if (target._config) {
Expand Down Expand Up @@ -309,6 +321,10 @@ export function createDeprecationProxy(instance) {
) {
deprecationConsoleWarning('firestore', prop, 'statics', false);
}
if (prop === 'CustomProvider') {
deprecationConsoleWarning('appCheck', prop, 'statics', false);
}

if (prop !== 'setLogLevel') {
// we want to capture setLogLevel function call which we do below
return Reflect.get(target, prop, receiver);
Expand Down
21 changes: 17 additions & 4 deletions packages/app/lib/common/unitTestUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ export type CheckV9DeprecationFunction = (
modularFunction: () => void,
nonModularFunction: () => void,
methodNameKey: string,
uniqueMessage: string = '',
uniqueMessage?: string | null,
ignoreFirebaseAppDeprecationWarning?: boolean,
) => void;

export const createCheckV9Deprecation = (moduleNames: string[]): CheckV9DeprecationFunction => {
Expand All @@ -28,14 +29,26 @@ export const createCheckV9Deprecation = (moduleNames: string[]): CheckV9Deprecat
nonModularFunction: () => void,
methodNameKey: string,
uniqueMessage: string?,
checkFirebaseAppDeprecationWarning: boolean = false,
) => {
const moduleName = moduleNames[0]; // firestore, database, etc
const instanceName = moduleNames[1] || 'default'; // default, FirestoreCollectionReference, etc
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
// Do not call `mockRestore()` as it removes the spy
const consoleWarnSpy = jest.spyOn(console, 'warn');
consoleWarnSpy.mockReset();

consoleWarnSpy.mockImplementation(warnMessage => {
const firebaseAppDeprecationMessage = warnMessage.includes('Please use `getApp()` instead.');
if (checkFirebaseAppDeprecationWarning) {
throw new Error(`Console warn was called unexpectedly with: ${warnMessage}`);
}

if (!firebaseAppDeprecationMessage) {
// we want to ignore all firebase app deprecation warnings (e.g. "Please use `getApp()` instead.") unless actually testing for it which we do above
throw new Error(`Console warn was called unexpectedly with: ${warnMessage}`);
}
});
// Do not call `mockRestore()` unless removing the spy
modularFunction();
expect(consoleWarnSpy).not.toHaveBeenCalled();
consoleWarnSpy.mockReset();
consoleWarnSpy.mockRestore();
const consoleWarnSpy2 = jest.spyOn(console, 'warn').mockImplementation(warnMessage => {
Expand Down

0 comments on commit 6b9ff91

Please sign in to comment.