Skip to content
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

chore(app-check): deprecation warning for v8 API ahead of future major release #8198

Draft
wants to merge 2 commits into
base: v8-deprecations
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
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 @@
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}`);

Check warning on line 42 in packages/app/lib/common/unitTestUtils.ts

View check run for this annotation

Codecov / codecov/patch

packages/app/lib/common/unitTestUtils.ts#L42

Added line #L42 was not covered by tests
} else {
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}`);

Check warning on line 46 in packages/app/lib/common/unitTestUtils.ts

View check run for this annotation

Codecov / codecov/patch

packages/app/lib/common/unitTestUtils.ts#L46

Added line #L46 was not covered by tests
}
}
});
// 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
Loading