Skip to content

Commit 3279cc1

Browse files
russellwheatleymikehardy
authored andcommitted
chore(crashlytics): deprecation warning for v8 API ahead of future major release (#8107)
1 parent e8eb163 commit 3279cc1

File tree

6 files changed

+279
-19
lines changed

6 files changed

+279
-19
lines changed

packages/app/lib/common/index.js

+58
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,64 @@ export function tryJSONStringify(data) {
103103
}
104104
}
105105

106+
// Used to indicate if there is no corresponding modular function
107+
const NO_REPLACEMENT = true;
108+
109+
const mapOfDeprecationReplacements = {
110+
crashlytics: {
111+
checkForUnsentReports: 'checkForUnsentReports()',
112+
crash: 'crash()',
113+
deleteUnsentReports: 'deleteUnsentReports()',
114+
didCrashOnPreviousExecution: 'didCrashOnPreviousExecution()',
115+
log: 'log()',
116+
setAttribute: 'setAttribute()',
117+
setAttributes: 'setAttributes()',
118+
setUserId: 'setUserId()',
119+
recordError: 'recordError()',
120+
sendUnsentReports: 'sendUnsentReports()',
121+
setCrashlyticsCollectionEnabled: 'setCrashlyticsCollectionEnabled()',
122+
},
123+
};
124+
125+
const v8deprecationMessage =
126+
'This v8 method is deprecated and will be removed in the next major release ' +
127+
'as part of move to match Firebase Web modular v9 SDK API.';
128+
129+
export function deprecationConsoleWarning(moduleName, methodName, isModularMethod) {
130+
if (!isModularMethod) {
131+
const moduleMap = mapOfDeprecationReplacements[moduleName];
132+
if (moduleMap) {
133+
const replacementMethodName = moduleMap[methodName];
134+
// only warn if it is mapped and purposefully deprecated
135+
if (replacementMethodName) {
136+
const message = createMessage(moduleName, methodName);
137+
138+
// eslint-disable-next-line no-console
139+
console.warn(message);
140+
}
141+
}
142+
}
143+
}
144+
145+
export function createMessage(moduleName, methodName, uniqueMessage = '') {
146+
if (uniqueMessage.length > 0) {
147+
// Unique deprecation message used for testing
148+
return uniqueMessage;
149+
}
150+
151+
const moduleMap = mapOfDeprecationReplacements[moduleName];
152+
if (moduleMap) {
153+
const replacementMethodName = moduleMap[methodName];
154+
if (replacementMethodName) {
155+
if (replacementMethodName !== NO_REPLACEMENT) {
156+
return v8deprecationMessage + ` Please use \`${replacementMethodName}\` instead.`;
157+
} else {
158+
return v8deprecationMessage;
159+
}
160+
}
161+
}
162+
}
163+
106164
export const MODULAR_DEPRECATION_ARG = 'react-native-firebase-modular-method-call';
107165

108166
export function warnIfNotModularCall(args, replacementMethodName, noAlternative) {
+38-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,46 @@
1+
// @ts-nocheck
12
import { expect, jest } from '@jest/globals';
3+
import { createMessage } from './index';
24

35
export const checkV9Deprecation = (modularFunction: () => void, nonModularFunction: () => void) => {
46
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
7+
consoleWarnSpy.mockRestore();
58
modularFunction();
69
expect(consoleWarnSpy).not.toHaveBeenCalled();
10+
consoleWarnSpy.mockClear();
11+
const consoleWarnSpy2 = jest.spyOn(console, 'warn').mockImplementation(() => {});
712
nonModularFunction();
8-
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
9-
consoleWarnSpy.mockRestore();
13+
14+
expect(consoleWarnSpy2).toHaveBeenCalledTimes(1);
15+
consoleWarnSpy2.mockClear();
16+
};
17+
18+
export type CheckV9DeprecationFunction = (
19+
modularFunction: () => void,
20+
nonModularFunction: () => void,
21+
methodName: string,
22+
uniqueMessage: string = '',
23+
) => void;
24+
25+
export const createCheckV9Deprecation = (moduleName: string): CheckV9DeprecationFunction => {
26+
return (
27+
modularFunction: () => void,
28+
nonModularFunction: () => void,
29+
methodName: string,
30+
uniqueMessage = '',
31+
) => {
32+
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
33+
consoleWarnSpy.mockReset();
34+
modularFunction();
35+
expect(consoleWarnSpy).not.toHaveBeenCalled();
36+
consoleWarnSpy.mockReset();
37+
const consoleWarnSpy2 = jest.spyOn(console, 'warn').mockImplementation(warnMessage => {
38+
const message = createMessage(moduleName, methodName, uniqueMessage);
39+
expect(message).toMatch(warnMessage);
40+
});
41+
nonModularFunction();
42+
43+
expect(consoleWarnSpy2).toHaveBeenCalledTimes(1);
44+
consoleWarnSpy2.mockReset();
45+
};
1046
};

packages/app/lib/internal/registry/namespace.js

+26-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*
1616
*/
1717

18-
import { isString } from '../../common';
18+
import { isString, MODULAR_DEPRECATION_ARG, deprecationConsoleWarning } from '../../common';
1919
import FirebaseApp from '../../FirebaseApp';
2020
import SDK_VERSION from '../../version';
2121
import { DEFAULT_APP_NAME, KNOWN_NAMESPACES } from '../constants';
@@ -170,10 +170,10 @@ function getOrCreateModuleForRoot(moduleNamespace) {
170170
}
171171

172172
if (!APP_MODULE_INSTANCE[_app.name][moduleNamespace]) {
173-
APP_MODULE_INSTANCE[_app.name][moduleNamespace] = new ModuleClass(
174-
_app,
175-
NAMESPACE_REGISTRY[moduleNamespace],
173+
const module = createDeprecationProxy(
174+
new ModuleClass(_app, NAMESPACE_REGISTRY[moduleNamespace]),
176175
);
176+
APP_MODULE_INSTANCE[_app.name][moduleNamespace] = module;
177177
}
178178

179179
return APP_MODULE_INSTANCE[_app.name][moduleNamespace];
@@ -277,6 +277,28 @@ export function getFirebaseRoot() {
277277
return createFirebaseRoot();
278278
}
279279

280+
function createDeprecationProxy(instance) {
281+
return new Proxy(instance, {
282+
get(target, prop, receiver) {
283+
const originalMethod = target[prop];
284+
if (prop === 'constructor') {
285+
return target.constructor;
286+
}
287+
if (typeof originalMethod === 'function') {
288+
return function (...args) {
289+
const isModularMethod = args.includes(MODULAR_DEPRECATION_ARG);
290+
const moduleName = receiver._config.namespace;
291+
292+
deprecationConsoleWarning(moduleName, prop, isModularMethod);
293+
294+
return originalMethod.apply(target, args);
295+
};
296+
}
297+
return Reflect.get(target, prop, receiver);
298+
},
299+
});
300+
}
301+
280302
/**
281303
*
282304
* @param options

packages/crashlytics/__tests__/crashlytics.test.ts

+135-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
import { describe, expect, it } from '@jest/globals';
2-
1+
import { describe, expect, it, jest, beforeEach } from '@jest/globals';
2+
// @ts-ignore test
3+
import FirebaseModule from '../../app/lib/internal/FirebaseModule';
4+
import {
5+
createCheckV9Deprecation,
6+
CheckV9DeprecationFunction,
7+
} from '../../app/lib/common/unitTestUtils';
38
import {
49
firebase,
510
getCrashlytics,
@@ -79,4 +84,132 @@ describe('Crashlytics', function () {
7984
expect(setCrashlyticsCollectionEnabled).toBeDefined();
8085
});
8186
});
87+
88+
describe('test `console.warn` is called for RNFB v8 API & not called for v9 API', function () {
89+
let checkV9Deprecation: CheckV9DeprecationFunction;
90+
91+
beforeEach(function () {
92+
checkV9Deprecation = createCheckV9Deprecation('crashlytics');
93+
94+
// @ts-ignore test
95+
jest.spyOn(FirebaseModule.prototype, 'native', 'get').mockImplementation(() => {
96+
return new Proxy(
97+
{},
98+
{
99+
get: () => jest.fn(),
100+
},
101+
);
102+
});
103+
});
104+
105+
it('checkForUnsentReports', function () {
106+
const crashlytics = getCrashlytics();
107+
checkV9Deprecation(
108+
() => checkForUnsentReports(crashlytics),
109+
() => crashlytics.checkForUnsentReports(),
110+
'checkForUnsentReports',
111+
);
112+
});
113+
114+
it('crash', function () {
115+
const crashlytics = getCrashlytics();
116+
checkV9Deprecation(
117+
() => crash(crashlytics),
118+
() => crashlytics.crash(),
119+
'crash',
120+
);
121+
});
122+
123+
it('deleteUnsentReports', function () {
124+
const crashlytics = getCrashlytics();
125+
checkV9Deprecation(
126+
() => deleteUnsentReports(crashlytics),
127+
() => crashlytics.deleteUnsentReports(),
128+
'deleteUnsentReports',
129+
);
130+
});
131+
132+
it('didCrashOnPreviousExecution', function () {
133+
const crashlytics = getCrashlytics();
134+
checkV9Deprecation(
135+
() => didCrashOnPreviousExecution(crashlytics),
136+
() => crashlytics.didCrashOnPreviousExecution(),
137+
'didCrashOnPreviousExecution',
138+
);
139+
});
140+
141+
it('log', function () {
142+
const crashlytics = getCrashlytics();
143+
checkV9Deprecation(
144+
() => log(crashlytics, 'message'),
145+
() => crashlytics.log('message'),
146+
'log',
147+
);
148+
});
149+
150+
it('setAttribute', function () {
151+
const crashlytics = getCrashlytics();
152+
checkV9Deprecation(
153+
() => setAttribute(crashlytics, 'name', 'value'),
154+
() => crashlytics.setAttribute('name', 'value'),
155+
'setAttribute',
156+
);
157+
});
158+
159+
it('setAttributes', function () {
160+
const crashlytics = getCrashlytics();
161+
checkV9Deprecation(
162+
() => setAttributes(crashlytics, {}),
163+
() => crashlytics.setAttributes({}),
164+
'setAttributes',
165+
);
166+
});
167+
168+
it('setUserId', function () {
169+
const crashlytics = getCrashlytics();
170+
checkV9Deprecation(
171+
() => setUserId(crashlytics, 'id'),
172+
() => crashlytics.setUserId('id'),
173+
'setUserId',
174+
);
175+
});
176+
177+
it('recordError', function () {
178+
const crashlytics = getCrashlytics();
179+
checkV9Deprecation(
180+
() => recordError(crashlytics, new Error(), 'name'),
181+
() => crashlytics.recordError(new Error(), 'name'),
182+
'recordError',
183+
);
184+
});
185+
186+
it('sendUnsentReports', function () {
187+
const crashlytics = getCrashlytics();
188+
checkV9Deprecation(
189+
() => sendUnsentReports(crashlytics),
190+
() => crashlytics.sendUnsentReports(),
191+
'sendUnsentReports',
192+
);
193+
});
194+
195+
it('setCrashlyticsCollectionEnabled', function () {
196+
const crashlytics = getCrashlytics();
197+
checkV9Deprecation(
198+
() => setCrashlyticsCollectionEnabled(crashlytics, true),
199+
() => crashlytics.setCrashlyticsCollectionEnabled(true),
200+
'setCrashlyticsCollectionEnabled',
201+
);
202+
});
203+
204+
it('isCrashlyticsCollectionEnabled', function () {
205+
const crashlytics = getCrashlytics();
206+
checkV9Deprecation(
207+
// swapped order here because we're deprecating the modular method and keeping the property on Crashlytics instance
208+
() => crashlytics.isCrashlyticsCollectionEnabled,
209+
() => isCrashlyticsCollectionEnabled(crashlytics),
210+
'',
211+
'`isCrashlyticsCollectionEnabled()` is deprecated, please use `Crashlytics.isCrashlyticsCollectionEnabled` property instead',
212+
);
213+
});
214+
});
82215
});

packages/crashlytics/lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class FirebaseCrashlyticsModule extends FirebaseModule {
5151
}
5252

5353
get isCrashlyticsCollectionEnabled() {
54+
// Purposefully did not deprecate this as I think it should remain a property rather than a method.
5455
return this._isCrashlyticsCollectionEnabled;
5556
}
5657

0 commit comments

Comments
 (0)