Skip to content

Commit 07075dd

Browse files
Multiple bug fixes
- Handle numeric custom signal values - Handle empty custom signal map - Log error instead of throwing when exceeding limits
1 parent 832258b commit 07075dd

File tree

7 files changed

+69
-46
lines changed

7 files changed

+69
-46
lines changed

packages/remote-config/src/api.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import {
2828
RC_CUSTOM_SIGNAL_KEY_MAX_LENGTH,
2929
RC_CUSTOM_SIGNAL_VALUE_MAX_LENGTH
3030
} from './constants';
31-
import { ERROR_FACTORY, ErrorCode, hasErrorCode } from './errors';
31+
import { ErrorCode, hasErrorCode } from './errors';
3232
import { RemoteConfig as RemoteConfigImpl } from './remote_config';
3333
import { Value as ValueImpl } from './value';
3434
import { LogLevel as FirebaseLogLevel } from '@firebase/logger';
@@ -270,8 +270,8 @@ function getAllKeys(obj1: {} = {}, obj2: {} = {}): string[] {
270270
*
271271
* @param remoteConfig - The {@link RemoteConfig} instance.
272272
* @param customSignals - Map (key, value) of the custom signals to be set for the app instance. If
273-
* a key already exists, the value is overwritten. Setting the value of a custom signal null unsets
274-
* the signal. The signals will be persisted locally on the client.
273+
* a key already exists, the value is overwritten. Setting the value of a custom signal to null
274+
* unsets the signal. The signals will be persisted locally on the client.
275275
*
276276
* @public
277277
*/
@@ -280,25 +280,35 @@ export async function setCustomSignals(
280280
customSignals: CustomSignals
281281
): Promise<void> {
282282
const rc = getModularInstance(remoteConfig) as RemoteConfigImpl;
283+
if (Object.keys(customSignals).length === 0) {
284+
return;
285+
}
286+
283287
// eslint-disable-next-line guard-for-in
284288
for (const key in customSignals) {
285289
if (key.length > RC_CUSTOM_SIGNAL_KEY_MAX_LENGTH) {
286-
throw ERROR_FACTORY.create(ErrorCode.CUSTOM_SIGNAL_KEY_LENGTH, {
287-
key,
288-
maxLength: RC_CUSTOM_SIGNAL_KEY_MAX_LENGTH
289-
});
290+
rc._logger.error(
291+
`Custom signal key ${key} is too long, max allowed length is ${RC_CUSTOM_SIGNAL_KEY_MAX_LENGTH}.`
292+
);
293+
return;
290294
}
291295
const value = customSignals[key];
292296
if (
293297
typeof value === 'string' &&
294298
value.length > RC_CUSTOM_SIGNAL_VALUE_MAX_LENGTH
295299
) {
296-
throw ERROR_FACTORY.create(ErrorCode.CUSTOM_SIGNAL_VALUE_LENGTH, {
297-
key,
298-
maxLength: RC_CUSTOM_SIGNAL_VALUE_MAX_LENGTH
299-
});
300+
rc._logger.error(
301+
`Value supplied for custom signal ${key} is too long, max allowed length is ${RC_CUSTOM_SIGNAL_VALUE_MAX_LENGTH}.`
302+
);
303+
return;
300304
}
301305
}
302306

303-
return rc._storageCache.setCustomSignals(customSignals);
307+
try {
308+
await rc._storageCache.setCustomSignals(customSignals);
309+
} catch (error) {
310+
rc._logger.error(
311+
`Error encountered while setting custom signals: ${error}`
312+
);
313+
}
304314
}

packages/remote-config/src/errors.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ export const enum ErrorCode {
3232
FETCH_PARSE = 'fetch-client-parse',
3333
FETCH_STATUS = 'fetch-status',
3434
INDEXED_DB_UNAVAILABLE = 'indexed-db-unavailable',
35-
CUSTOM_SIGNAL_MAX_ALLOWED_SIGNALS = 'custom-signal-max-allowed-signals',
36-
CUSTOM_SIGNAL_KEY_LENGTH = 'custom-signal-key-length',
37-
CUSTOM_SIGNAL_VALUE_LENGTH = 'custom-signal-value-length'
35+
CUSTOM_SIGNAL_MAX_ALLOWED_SIGNALS = 'custom-signal-max-allowed-signals'
3836
}
3937

4038
const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = {
@@ -72,11 +70,7 @@ const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = {
7270
[ErrorCode.INDEXED_DB_UNAVAILABLE]:
7371
'Indexed DB is not supported by current browser',
7472
[ErrorCode.CUSTOM_SIGNAL_MAX_ALLOWED_SIGNALS]:
75-
'Setting more than {$maxSignals} custom signals is not supported.',
76-
[ErrorCode.CUSTOM_SIGNAL_KEY_LENGTH]:
77-
'Custom signal key {$key} is too long, max allowed length is {$maxLength}.',
78-
[ErrorCode.CUSTOM_SIGNAL_VALUE_LENGTH]:
79-
'Value supplied for custom signal {$key} is too long, max allowed length is {$maxLength}.'
73+
'Setting more than {$maxSignals} custom signals is not supported.'
8074
};
8175

8276
// Note this is effectively a type system binding a code to params. This approach overlaps with the
@@ -96,8 +90,6 @@ interface ErrorParams {
9690
[ErrorCode.FETCH_PARSE]: { originalErrorMessage: string };
9791
[ErrorCode.FETCH_STATUS]: { httpStatus: number };
9892
[ErrorCode.CUSTOM_SIGNAL_MAX_ALLOWED_SIGNALS]: { maxSignals: number };
99-
[ErrorCode.CUSTOM_SIGNAL_KEY_LENGTH]: { key: string; maxLength: number };
100-
[ErrorCode.CUSTOM_SIGNAL_VALUE_LENGTH]: { key: string; maxLength: number };
10193
}
10294

10395
export const ERROR_FACTORY = new ErrorFactory<ErrorCode, ErrorParams>(

packages/remote-config/src/storage/storage.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ export class Storage {
187187
return this.get<CustomSignals>('custom_signals');
188188
}
189189

190-
async setCustomSignals(customSignals: CustomSignals): Promise<void> {
190+
async setCustomSignals(customSignals: CustomSignals): Promise<CustomSignals> {
191191
const db = await this.openDbPromise;
192192
const transaction = db.transaction([APP_NAMESPACE_STORE], 'readwrite');
193193
const storedSignals = await this.getWithTransaction<CustomSignals>(
@@ -199,24 +199,34 @@ export class Storage {
199199
...customSignals
200200
};
201201
// Filter out key-value assignments with null values since they are signals being unset
202-
const signalsToUpdate = Object.fromEntries(
203-
Object.entries(combinedSignals).filter(([_, v]) => v !== null)
202+
const updatedSignals = Object.fromEntries(
203+
Object.entries(combinedSignals)
204+
.filter(([_, v]) => v !== null)
205+
.map(([k, v]) => {
206+
// Stringify numbers to store a map of string keys and values which can be sent
207+
// as-is in a fetch call.
208+
if (typeof v === 'number') {
209+
return [k, v.toString()];
210+
}
211+
return [k, v];
212+
})
204213
);
205214

206215
// Throw an error if the number of custom signals to be stored exceeds the limit
207216
if (
208-
Object.keys(signalsToUpdate).length > RC_CUSTOM_SIGNAL_MAX_ALLOWED_SIGNALS
217+
Object.keys(updatedSignals).length > RC_CUSTOM_SIGNAL_MAX_ALLOWED_SIGNALS
209218
) {
210219
throw ERROR_FACTORY.create(ErrorCode.CUSTOM_SIGNAL_MAX_ALLOWED_SIGNALS, {
211220
maxSignals: RC_CUSTOM_SIGNAL_MAX_ALLOWED_SIGNALS
212221
});
213222
}
214223

215-
return this.setWithTransaction<CustomSignals>(
224+
await this.setWithTransaction<CustomSignals>(
216225
'custom_signals',
217-
signalsToUpdate,
226+
updatedSignals,
218227
transaction
219228
);
229+
return updatedSignals;
220230
}
221231

222232
/**

packages/remote-config/src/storage/storage_cache.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,7 @@ export class StorageCache {
111111
return this.storage.setActiveConfig(activeConfig);
112112
}
113113

114-
setCustomSignals(customSignals: CustomSignals): Promise<void> {
115-
this.customSignals = customSignals;
116-
return this.storage.setCustomSignals(customSignals);
114+
async setCustomSignals(customSignals: CustomSignals): Promise<void> {
115+
this.customSignals = await this.storage.setCustomSignals(customSignals);
117116
}
118117
}

packages/remote-config/test/remote_config.test.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ describe('RemoteConfig', () => {
9898
beforeEach(() => {
9999
storageCache.setCustomSignals = sinon.stub();
100100
storage.setCustomSignals = sinon.stub();
101+
logger.error = sinon.stub();
101102
});
102103

103104
it('call storage API to store signals', async () => {
@@ -108,26 +109,30 @@ describe('RemoteConfig', () => {
108109
});
109110
});
110111

111-
it('throws an error when supplied with a custom signal key greater than 250 characters', async () => {
112+
it('logs an error when supplied with a custom signal key greater than 250 characters', async () => {
112113
const longKey = 'a'.repeat(251);
113114
const customSignals = { [longKey]: 'value' };
114115

115-
await expect(
116-
setCustomSignals(rc, customSignals)
117-
).to.eventually.be.rejectedWith(
118-
`Remote Config: Custom signal key ${longKey} is too long, max allowed length is 250.`
119-
);
116+
await setCustomSignals(rc, customSignals);
117+
118+
expect(storageCache.setCustomSignals).to.not.have.been.called;
119+
expect(logger.error).to.have.been.called;
120120
});
121121

122-
it('throws an error when supplied with a custom signal value greater than 500 characters', async () => {
122+
it('logs an error when supplied with a custom signal value greater than 500 characters', async () => {
123123
const longValue = 'a'.repeat(501);
124124
const customSignals = { 'key': longValue };
125125

126-
await expect(
127-
setCustomSignals(rc, customSignals)
128-
).to.eventually.be.rejectedWith(
129-
'Remote Config: Value supplied for custom signal key is too long, max allowed length is 500.'
130-
);
126+
await setCustomSignals(rc, customSignals);
127+
128+
expect(storageCache.setCustomSignals).to.not.have.been.called;
129+
expect(logger.error).to.have.been.called;
130+
});
131+
132+
it('empty custom signals map does nothing', async () => {
133+
await setCustomSignals(rc, {});
134+
135+
expect(storageCache.setCustomSignals).to.not.have.been.called;
131136
});
132137
});
133138

packages/remote-config/test/storage/storage.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,18 @@ describe('Storage', () => {
119119
});
120120

121121
it('sets and gets custom signals', async () => {
122-
const customSignals = { key: 'value', key1: 'value1' };
122+
const customSignals = { key: 'value', key1: 'value1', key2: 1 };
123+
const customSignalsInStorage = {
124+
key: 'value',
125+
key1: 'value1',
126+
key2: '1'
127+
};
123128

124129
await storage.setCustomSignals(customSignals);
125130

126131
const storedCustomSignals = await storage.getCustomSignals();
127132

128-
expect(storedCustomSignals).to.deep.eq(customSignals);
133+
expect(storedCustomSignals).to.deep.eq(customSignalsInStorage);
129134
});
130135

131136
it('upserts custom signals when key is present in storage', async () => {

packages/remote-config/test/storage/storage_cache.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ describe('StorageCache', () => {
9191
const customSignals = { key: 'value' };
9292

9393
beforeEach(() => {
94-
storage.setCustomSignals = sinon.stub().returns(Promise.resolve());
94+
storage.setCustomSignals = sinon
95+
.stub()
96+
.returns(Promise.resolve(customSignals));
9597
});
9698

9799
it('writes to memory cache', async () => {

0 commit comments

Comments
 (0)