Skip to content

Commit 94f9785

Browse files
committed
Switch to all mutation
1 parent e12e6ef commit 94f9785

File tree

9 files changed

+188
-147
lines changed

9 files changed

+188
-147
lines changed

packages/app-check/src/api.test.ts

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717
import '../test/setup';
1818
import { expect } from 'chai';
19-
import { spy, stub } from 'sinon';
19+
import { SinonStub, spy, stub } from 'sinon';
2020
import {
2121
setTokenAutoRefreshEnabled,
2222
initializeAppCheck,
@@ -31,7 +31,12 @@ import {
3131
getFakeAppCheck,
3232
removegreCAPTCHAScriptsOnPage
3333
} from '../test/util';
34-
import { clearState, getState } from './state';
34+
import {
35+
clearState,
36+
DEFAULT_STATE,
37+
getStateReference,
38+
setInitialState
39+
} from './state';
3540
import * as reCAPTCHA from './recaptcha';
3641
import * as util from './util';
3742
import * as logger from './logger';
@@ -52,15 +57,21 @@ import { getDebugToken } from './debug';
5257

5358
describe('api', () => {
5459
let app: FirebaseApp;
60+
let storageReadStub: SinonStub;
61+
let storageWriteStub: SinonStub;
5562

5663
beforeEach(() => {
5764
app = getFullApp();
65+
storageReadStub = stub(storage, 'readTokenFromStorage').resolves(undefined);
66+
storageWriteStub = stub(storage, 'writeTokenToStorage');
5867
stub(util, 'getRecaptcha').returns(getFakeGreCAPTCHA());
5968
});
6069

61-
afterEach(() => {
70+
afterEach(async () => {
6271
clearState();
6372
removegreCAPTCHAScriptsOnPage();
73+
storageReadStub.restore();
74+
storageWriteStub.restore();
6475
return deleteApp(app);
6576
});
6677

@@ -216,19 +227,19 @@ describe('api', () => {
216227
});
217228

218229
it('sets activated to true', () => {
219-
expect(getState(app).activated).to.equal(false);
230+
expect(getStateReference(app).activated).to.equal(false);
220231
initializeAppCheck(app, {
221232
provider: new ReCaptchaV3Provider(FAKE_SITE_KEY)
222233
});
223-
expect(getState(app).activated).to.equal(true);
234+
expect(getStateReference(app).activated).to.equal(true);
224235
});
225236

226237
it('isTokenAutoRefreshEnabled value defaults to global setting', () => {
227238
app.automaticDataCollectionEnabled = false;
228239
initializeAppCheck(app, {
229240
provider: new ReCaptchaV3Provider(FAKE_SITE_KEY)
230241
});
231-
expect(getState(app).isTokenAutoRefreshEnabled).to.equal(false);
242+
expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(false);
232243
});
233244

234245
it('sets isTokenAutoRefreshEnabled correctly, overriding global setting', () => {
@@ -237,15 +248,16 @@ describe('api', () => {
237248
provider: new ReCaptchaV3Provider(FAKE_SITE_KEY),
238249
isTokenAutoRefreshEnabled: true
239250
});
240-
expect(getState(app).isTokenAutoRefreshEnabled).to.equal(true);
251+
expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(true);
241252
});
242253
});
243254
describe('setTokenAutoRefreshEnabled()', () => {
244255
it('sets isTokenAutoRefreshEnabled correctly', () => {
245256
const app = getFakeApp({ automaticDataCollectionEnabled: false });
246257
const appCheck = getFakeAppCheck(app);
258+
setInitialState(app, { ...DEFAULT_STATE });
247259
setTokenAutoRefreshEnabled(appCheck, true);
248-
expect(getState(app).isTokenAutoRefreshEnabled).to.equal(true);
260+
expect(getStateReference(app).isTokenAutoRefreshEnabled).to.equal(true);
249261
});
250262
});
251263
describe('getToken()', () => {
@@ -279,7 +291,7 @@ describe('api', () => {
279291
isTokenAutoRefreshEnabled: true
280292
});
281293

282-
expect(getState(app).tokenObservers.length).to.equal(1);
294+
expect(getStateReference(app).tokenObservers.length).to.equal(1);
283295

284296
const fakeRecaptchaToken = 'fake-recaptcha-token';
285297
const fakeRecaptchaAppCheckToken = {
@@ -291,7 +303,6 @@ describe('api', () => {
291303
stub(client, 'exchangeToken').returns(
292304
Promise.resolve(fakeRecaptchaAppCheckToken)
293305
);
294-
stub(storage, 'writeTokenToStorage').returns(Promise.resolve(undefined));
295306

296307
const listener1 = stub().throws(new Error());
297308
const listener2 = spy();
@@ -302,7 +313,7 @@ describe('api', () => {
302313
const unsubscribe1 = onTokenChanged(appCheck, listener1, errorFn1);
303314
const unsubscribe2 = onTokenChanged(appCheck, listener2, errorFn2);
304315

305-
expect(getState(app).tokenObservers.length).to.equal(3);
316+
expect(getStateReference(app).tokenObservers.length).to.equal(3);
306317

307318
await internalApi.getToken(appCheck as AppCheckService);
308319

@@ -315,7 +326,7 @@ describe('api', () => {
315326
expect(errorFn2).to.not.be.called;
316327
unsubscribe1();
317328
unsubscribe2();
318-
expect(getState(app).tokenObservers.length).to.equal(1);
329+
expect(getStateReference(app).tokenObservers.length).to.equal(1);
319330
});
320331

321332
it('Listeners work when using Observer pattern', async () => {
@@ -324,7 +335,7 @@ describe('api', () => {
324335
isTokenAutoRefreshEnabled: true
325336
});
326337

327-
expect(getState(app).tokenObservers.length).to.equal(1);
338+
expect(getStateReference(app).tokenObservers.length).to.equal(1);
328339

329340
const fakeRecaptchaToken = 'fake-recaptcha-token';
330341
const fakeRecaptchaAppCheckToken = {
@@ -336,7 +347,7 @@ describe('api', () => {
336347
stub(client, 'exchangeToken').returns(
337348
Promise.resolve(fakeRecaptchaAppCheckToken)
338349
);
339-
stub(storage, 'writeTokenToStorage').returns(Promise.resolve(undefined));
350+
storageWriteStub.returns(Promise.resolve(undefined));
340351

341352
const listener1 = stub().throws(new Error());
342353
const listener2 = spy();
@@ -357,7 +368,7 @@ describe('api', () => {
357368
error: errorFn1
358369
});
359370

360-
expect(getState(app).tokenObservers.length).to.equal(3);
371+
expect(getStateReference(app).tokenObservers.length).to.equal(3);
361372

362373
await internalApi.getToken(appCheck as AppCheckService);
363374

@@ -370,7 +381,7 @@ describe('api', () => {
370381
expect(errorFn2).to.not.be.called;
371382
unsubscribe1();
372383
unsubscribe2();
373-
expect(getState(app).tokenObservers.length).to.equal(1);
384+
expect(getStateReference(app).tokenObservers.length).to.equal(1);
374385
});
375386

376387
it('onError() catches token errors', async () => {
@@ -380,12 +391,12 @@ describe('api', () => {
380391
isTokenAutoRefreshEnabled: false
381392
});
382393

383-
expect(getState(app).tokenObservers.length).to.equal(0);
394+
expect(getStateReference(app).tokenObservers.length).to.equal(0);
384395

385396
const fakeRecaptchaToken = 'fake-recaptcha-token';
386397
stub(reCAPTCHA, 'getToken').returns(Promise.resolve(fakeRecaptchaToken));
387398
stub(client, 'exchangeToken').rejects('exchange error');
388-
stub(storage, 'writeTokenToStorage').returns(Promise.resolve(undefined));
399+
storageWriteStub.returns(Promise.resolve(undefined));
389400

390401
const listener1 = spy();
391402

@@ -395,13 +406,13 @@ describe('api', () => {
395406

396407
await internalApi.getToken(appCheck as AppCheckService);
397408

398-
expect(getState(app).tokenObservers.length).to.equal(1);
409+
expect(getStateReference(app).tokenObservers.length).to.equal(1);
399410

400411
expect(errorFn1).to.be.calledOnce;
401412
expect(errorFn1.args[0][0].name).to.include('exchange error');
402413

403414
unsubscribe1();
404-
expect(getState(app).tokenObservers.length).to.equal(0);
415+
expect(getStateReference(app).tokenObservers.length).to.equal(0);
405416
});
406417
});
407418
});

packages/app-check/src/api.ts

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,10 @@ import {
2424
} from './public-types';
2525
import { ERROR_FACTORY, AppCheckError } from './errors';
2626
import {
27-
getState,
28-
setState,
29-
AppCheckState,
27+
getStateReference,
3028
getDebugState,
31-
setStateProperty
29+
DEFAULT_STATE,
30+
setInitialState
3231
} from './state';
3332
import { FirebaseApp, getApp, _getProvider } from '@firebase/app';
3433
import { getModularInstance, ErrorFn, NextFn } from '@firebase/util';
@@ -107,7 +106,7 @@ export function initializeAppCheck(
107106
// If isTokenAutoRefreshEnabled is false, do not send any requests to the
108107
// exchange endpoint without an explicit call from the user either directly
109108
// or through another Firebase library (storage, functions, etc.)
110-
if (getState(app).isTokenAutoRefreshEnabled) {
109+
if (getStateReference(app).isTokenAutoRefreshEnabled) {
111110
// Adding a listener will start the refresher and fetch a token if needed.
112111
// This gets a token ready and prevents a delay when an internal library
113112
// requests the token.
@@ -134,13 +133,15 @@ function _activate(
134133
provider: AppCheckProvider,
135134
isTokenAutoRefreshEnabled?: boolean
136135
): void {
137-
const state = getState(app);
136+
// Create an entry in the APP_CHECK_STATES map. Further changes should
137+
// directly mutate this object.
138+
const state = setInitialState(app, { ...DEFAULT_STATE });
138139

139-
const newState: AppCheckState = { ...state, activated: true };
140-
newState.provider = provider; // Read cached token from storage if it exists and store it in memory.
141-
newState.cachedTokenPromise = readTokenFromStorage(app).then(cachedToken => {
140+
state.activated = true;
141+
state.provider = provider; // Read cached token from storage if it exists and store it in memory.
142+
state.cachedTokenPromise = readTokenFromStorage(app).then(cachedToken => {
142143
if (cachedToken && isValid(cachedToken)) {
143-
setStateProperty(app, 'token', cachedToken);
144+
state.token = cachedToken;
144145
// notify all listeners with the cached token
145146
notifyTokenListeners(app, { token: cachedToken.token });
146147
}
@@ -150,14 +151,12 @@ function _activate(
150151
// Use value of global `automaticDataCollectionEnabled` (which
151152
// itself defaults to false if not specified in config) if
152153
// `isTokenAutoRefreshEnabled` param was not provided by user.
153-
newState.isTokenAutoRefreshEnabled =
154+
state.isTokenAutoRefreshEnabled =
154155
isTokenAutoRefreshEnabled === undefined
155156
? app.automaticDataCollectionEnabled
156157
: isTokenAutoRefreshEnabled;
157158

158-
setState(app, newState);
159-
160-
newState.provider.initialize(app);
159+
state.provider.initialize(app);
161160
}
162161

163162
/**
@@ -174,7 +173,7 @@ export function setTokenAutoRefreshEnabled(
174173
isTokenAutoRefreshEnabled: boolean
175174
): void {
176175
const app = appCheckInstance.app;
177-
const state = getState(app);
176+
const state = getStateReference(app);
178177
// This will exist if any product libraries have called
179178
// `addTokenListener()`
180179
if (state.tokenRefresher) {
@@ -184,7 +183,7 @@ export function setTokenAutoRefreshEnabled(
184183
state.tokenRefresher.stop();
185184
}
186185
}
187-
setStateProperty(app, 'isTokenAutoRefreshEnabled', isTokenAutoRefreshEnabled);
186+
state.isTokenAutoRefreshEnabled = isTokenAutoRefreshEnabled;
188187
}
189188
/**
190189
* Get the current App Check token. Attaches to the most recent

packages/app-check/src/factory.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
removeTokenListener
2525
} from './internal-api';
2626
import { Provider } from '@firebase/component';
27-
import { getState } from './state';
27+
import { getStateReference } from './state';
2828

2929
/**
3030
* AppCheck Service class.
@@ -35,7 +35,7 @@ export class AppCheckService implements AppCheck, _FirebaseService {
3535
public heartbeatServiceProvider: Provider<'heartbeat'>
3636
) {}
3737
_delete(): Promise<void> {
38-
const { tokenObservers } = getState(this.app);
38+
const { tokenObservers } = getStateReference(this.app);
3939
for (const tokenObserver of tokenObservers) {
4040
removeTokenListener(this.app, tokenObserver.next);
4141
}

0 commit comments

Comments
 (0)