@react-native-firebase/remote-config 21.7.4 → 21.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/android/src/main/java/io/invertase/firebase/config/UniversalFirebaseConfigModule.java +36 -0
- package/android/src/reactnative/java/io/invertase/firebase/config/ReactNativeFirebaseConfigModule.java +14 -0
- package/ios/RNFBConfig/RNFBConfigModule.m +18 -0
- package/lib/modular/index.d.ts +23 -0
- package/lib/modular/index.js +18 -0
- package/lib/version.js +1 -1
- package/lib/web/RNFBConfigModule.js +8 -0
- package/package.json +4 -4
- package/__tests__/remote-config.test.ts +0 -220
package/CHANGELOG.md
CHANGED
@@ -3,6 +3,16 @@
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
5
5
|
|
6
|
+
## [21.8.0](https://github.com/invertase/react-native-firebase/compare/v21.7.4...v21.8.0) (2025-02-10)
|
7
|
+
|
8
|
+
### Features
|
9
|
+
|
10
|
+
- **remote-config:** support for custom signals ([#8274](https://github.com/invertase/react-native-firebase/issues/8274)) ([94c6a27](https://github.com/invertase/react-native-firebase/commit/94c6a27e6692138cd32394218a492d17d68de0f0))
|
11
|
+
|
12
|
+
### Bug Fixes
|
13
|
+
|
14
|
+
- do not ship unit tests in released packages ([e71dadf](https://github.com/invertase/react-native-firebase/commit/e71dadfc1c0cad2e89c94100913af31ddf7d9c91))
|
15
|
+
|
6
16
|
## [21.7.4](https://github.com/invertase/react-native-firebase/compare/v21.7.3...v21.7.4) (2025-02-08)
|
7
17
|
|
8
18
|
### Bug Fixes
|
package/android/src/main/java/io/invertase/firebase/config/UniversalFirebaseConfigModule.java
CHANGED
@@ -25,8 +25,10 @@ import android.content.res.Resources;
|
|
25
25
|
import android.content.res.XmlResourceParser;
|
26
26
|
import android.os.Bundle;
|
27
27
|
import com.google.android.gms.tasks.Task;
|
28
|
+
import com.google.android.gms.tasks.TaskCompletionSource;
|
28
29
|
import com.google.android.gms.tasks.Tasks;
|
29
30
|
import com.google.firebase.FirebaseApp;
|
31
|
+
import com.google.firebase.remoteconfig.CustomSignals;
|
30
32
|
import com.google.firebase.remoteconfig.FirebaseRemoteConfig;
|
31
33
|
import com.google.firebase.remoteconfig.FirebaseRemoteConfigInfo;
|
32
34
|
import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings;
|
@@ -199,6 +201,40 @@ public class UniversalFirebaseConfigModule extends UniversalFirebaseModule {
|
|
199
201
|
return convertedConfigBundle;
|
200
202
|
}
|
201
203
|
|
204
|
+
Task<Void> setCustomSignals(String appName, HashMap<String, Object> customSignalsMap) {
|
205
|
+
TaskCompletionSource<Void> taskCompletionSource = new TaskCompletionSource<>();
|
206
|
+
|
207
|
+
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
|
208
|
+
|
209
|
+
getExecutor().execute(
|
210
|
+
() -> {
|
211
|
+
try {
|
212
|
+
CustomSignals.Builder customSignals = new CustomSignals.Builder();
|
213
|
+
for (Map.Entry<String, Object> entry : customSignalsMap.entrySet()) {
|
214
|
+
Object value = entry.getValue();
|
215
|
+
if (value instanceof String) {
|
216
|
+
customSignals.put(entry.getKey(), (String) value);
|
217
|
+
} else if (value instanceof Long) {
|
218
|
+
customSignals.put(entry.getKey(), (Long) value);
|
219
|
+
} else if (value instanceof Integer) {
|
220
|
+
customSignals.put(entry.getKey(), ((Integer) value).longValue());
|
221
|
+
} else if (value instanceof Double) {
|
222
|
+
customSignals.put(entry.getKey(), (Double) value);
|
223
|
+
} else if (value == null) {
|
224
|
+
customSignals.put(entry.getKey(), null);
|
225
|
+
}
|
226
|
+
}
|
227
|
+
|
228
|
+
Tasks.await(FirebaseRemoteConfig.getInstance(firebaseApp).setCustomSignals(customSignals.build()));
|
229
|
+
taskCompletionSource.setResult(null);
|
230
|
+
} catch (Exception e) {
|
231
|
+
taskCompletionSource.setException(e);
|
232
|
+
}
|
233
|
+
});
|
234
|
+
|
235
|
+
return taskCompletionSource.getTask();
|
236
|
+
}
|
237
|
+
|
202
238
|
public Map<String, Object> getConstantsForApp(String appName) {
|
203
239
|
Map<String, Object> appConstants = new HashMap<>();
|
204
240
|
FirebaseRemoteConfigInfo remoteConfigInfo =
|
@@ -255,6 +255,20 @@ public class ReactNativeFirebaseConfigModule extends ReactNativeFirebaseModule {
|
|
255
255
|
}
|
256
256
|
}
|
257
257
|
|
258
|
+
@ReactMethod
|
259
|
+
public void setCustomSignals(String appName, ReadableMap customSignals, Promise promise) {
|
260
|
+
module
|
261
|
+
.setCustomSignals(appName, customSignals.toHashMap())
|
262
|
+
.addOnCompleteListener(
|
263
|
+
task -> {
|
264
|
+
if (task.isSuccessful()) {
|
265
|
+
promise.resolve(resultWithConstants(task.getResult()));
|
266
|
+
} else {
|
267
|
+
rejectPromiseWithExceptionMap(promise, task.getException());
|
268
|
+
}
|
269
|
+
});
|
270
|
+
}
|
271
|
+
|
258
272
|
private WritableMap resultWithConstants(Object result) {
|
259
273
|
Map<String, Object> responseMap = new HashMap<>(2);
|
260
274
|
responseMap.put("result", result);
|
@@ -305,6 +305,24 @@ RCT_EXPORT_METHOD(removeConfigUpdateRegistration : (FIRApp *)firebaseApp) {
|
|
305
305
|
}
|
306
306
|
}
|
307
307
|
|
308
|
+
RCT_EXPORT_METHOD(setCustomSignals
|
309
|
+
: (FIRApp *)firebaseApp
|
310
|
+
: (NSDictionary *)customSignals
|
311
|
+
: (RCTPromiseResolveBlock)resolve
|
312
|
+
: (RCTPromiseRejectBlock)reject) {
|
313
|
+
[[FIRRemoteConfig remoteConfigWithApp:firebaseApp]
|
314
|
+
setCustomSignals:customSignals
|
315
|
+
withCompletion:^(NSError *_Nullable error) {
|
316
|
+
if (error != nil) {
|
317
|
+
[RNFBSharedUtils rejectPromiseWithNSError:reject error:error];
|
318
|
+
} else {
|
319
|
+
resolve(nil);
|
320
|
+
}
|
321
|
+
}];
|
322
|
+
|
323
|
+
resolve([self resultWithConstants:[NSNull null] firebaseApp:firebaseApp]);
|
324
|
+
}
|
325
|
+
|
308
326
|
#pragma mark -
|
309
327
|
#pragma mark Internal Helper Methods
|
310
328
|
|
package/lib/modular/index.d.ts
CHANGED
@@ -207,3 +207,26 @@ export function onConfigUpdated(
|
|
207
207
|
remoteConfig: RemoteConfig,
|
208
208
|
callback: (config: ConfigValues) => void,
|
209
209
|
): () => void;
|
210
|
+
|
211
|
+
/**
|
212
|
+
* Defines the type for representing custom signals and their values.
|
213
|
+
* The values in CustomSignals must be one of the following types: string, number, or null.
|
214
|
+
* There are additional limitations on key and value length, for a full description see https://firebase.google.com/docs/remote-config/parameters?template_type=client#custom_signal_conditions
|
215
|
+
* Failing to stay within these limitations will result in a silent API failure with only a warning in device logs
|
216
|
+
*/
|
217
|
+
|
218
|
+
export interface CustomSignals {
|
219
|
+
[key: string]: string | number | null;
|
220
|
+
}
|
221
|
+
|
222
|
+
/**
|
223
|
+
* Sets the custom signals for the app instance.
|
224
|
+
* @param {RemoteConfig} remoteConfig - RemoteConfig instance
|
225
|
+
* @param {CustomSignals} customSignals - CustomSignals
|
226
|
+
* @returns {Promise<void>}
|
227
|
+
*/
|
228
|
+
|
229
|
+
export declare function setCustomSignals(
|
230
|
+
remoteConfig: RemoteConfig,
|
231
|
+
customSignals: CustomSignals,
|
232
|
+
): Promise<void>;
|
package/lib/modular/index.js
CHANGED
@@ -26,6 +26,7 @@ import { getApp } from '@react-native-firebase/app';
|
|
26
26
|
* @typedef {import('..').FirebaseRemoteConfigTypes.ConfigValues} ConfigValues
|
27
27
|
* @typedef {import('..').FirebaseRemoteConfigTypes.LastFetchStatusType} LastFetchStatusType
|
28
28
|
* @typedef {import('..').FirebaseRemoteConfigTypes.RemoteConfigLogLevel} RemoteConfigLogLevel
|
29
|
+
* @typedef {import('.').CustomSignals} CustomSignals
|
29
30
|
*/
|
30
31
|
|
31
32
|
/**
|
@@ -243,3 +244,20 @@ export function setDefaultsFromResource(remoteConfig, resourceName) {
|
|
243
244
|
export function onConfigUpdated(remoteConfig, callback) {
|
244
245
|
return remoteConfig.onConfigUpdated(callback);
|
245
246
|
}
|
247
|
+
|
248
|
+
/**
|
249
|
+
* Sets the custom signals for the app instance.
|
250
|
+
* @param {RemoteConfig} remoteConfig - RemoteConfig instance
|
251
|
+
* @param {CustomSignals} customSignals - CustomSignals
|
252
|
+
* @returns {Promise<void>}
|
253
|
+
*/
|
254
|
+
export async function setCustomSignals(remoteConfig, customSignals) {
|
255
|
+
for (const [key, value] of Object.entries(customSignals)) {
|
256
|
+
if (typeof value !== 'string' && typeof value !== 'number' && value !== null) {
|
257
|
+
throw new Error(
|
258
|
+
`firebase.remoteConfig().setCustomSignals(): Invalid type for custom signal '${key}': ${typeof value}. Expected 'string', 'number', or 'null'.`,
|
259
|
+
);
|
260
|
+
}
|
261
|
+
}
|
262
|
+
return remoteConfig._promiseWithConstants(remoteConfig.native.setCustomSignals(customSignals));
|
263
|
+
}
|
package/lib/version.js
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
// Generated by genversion.
|
2
|
-
module.exports = '21.
|
2
|
+
module.exports = '21.8.0';
|
@@ -7,6 +7,7 @@ import {
|
|
7
7
|
fetchConfig,
|
8
8
|
getAll,
|
9
9
|
makeIDBAvailable,
|
10
|
+
setCustomSignals,
|
10
11
|
} from '@react-native-firebase/app/lib/internal/web/firebaseRemoteConfig';
|
11
12
|
import { guard } from '@react-native-firebase/app/lib/internal/web/utils';
|
12
13
|
|
@@ -113,4 +114,11 @@ export default {
|
|
113
114
|
return resultAndConstants(remoteConfig, null);
|
114
115
|
});
|
115
116
|
},
|
117
|
+
setCustomSignals(appName, customSignals) {
|
118
|
+
return guard(async () => {
|
119
|
+
const remoteConfig = getRemoteConfigInstanceForApp(appName);
|
120
|
+
await setCustomSignals(remoteConfig, customSignals);
|
121
|
+
return resultAndConstants(remoteConfig, null);
|
122
|
+
});
|
123
|
+
},
|
116
124
|
};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@react-native-firebase/remote-config",
|
3
|
-
"version": "21.
|
3
|
+
"version": "21.8.0",
|
4
4
|
"author": "Invertase <oss@invertase.io> (http://invertase.io)",
|
5
5
|
"description": "React Native Firebase - React Native Firebase provides native integration with Remote Config, allowing you to change the appearance and/or functionality of your app without requiring an app update.",
|
6
6
|
"main": "lib/index.js",
|
@@ -24,11 +24,11 @@
|
|
24
24
|
"remote-config"
|
25
25
|
],
|
26
26
|
"peerDependencies": {
|
27
|
-
"@react-native-firebase/analytics": "21.
|
28
|
-
"@react-native-firebase/app": "21.
|
27
|
+
"@react-native-firebase/analytics": "21.8.0",
|
28
|
+
"@react-native-firebase/app": "21.8.0"
|
29
29
|
},
|
30
30
|
"publishConfig": {
|
31
31
|
"access": "public"
|
32
32
|
},
|
33
|
-
"gitHead": "
|
33
|
+
"gitHead": "14ebd5c2d2e0f284397c331f909e533c47f2ec62"
|
34
34
|
}
|
@@ -1,220 +0,0 @@
|
|
1
|
-
/*
|
2
|
-
* Copyright (c) 2016-present Invertase Limited & Contributors
|
3
|
-
*
|
4
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
-
* you may not use this library except in compliance with the License.
|
6
|
-
* You may obtain a copy of the License at
|
7
|
-
*
|
8
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
9
|
-
*
|
10
|
-
* Unless required by applicable law or agreed to in writing, software
|
11
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
-
* See the License for the specific language governing permissions and
|
14
|
-
* limitations under the License.
|
15
|
-
*
|
16
|
-
*/
|
17
|
-
import { describe, expect, it } from '@jest/globals';
|
18
|
-
|
19
|
-
import {
|
20
|
-
firebase,
|
21
|
-
getRemoteConfig,
|
22
|
-
activate,
|
23
|
-
ensureInitialized,
|
24
|
-
fetchAndActivate,
|
25
|
-
fetchConfig,
|
26
|
-
getAll,
|
27
|
-
getBoolean,
|
28
|
-
getNumber,
|
29
|
-
getString,
|
30
|
-
getValue,
|
31
|
-
setLogLevel,
|
32
|
-
isSupported,
|
33
|
-
fetchTimeMillis,
|
34
|
-
settings,
|
35
|
-
lastFetchStatus,
|
36
|
-
reset,
|
37
|
-
setConfigSettings,
|
38
|
-
fetch,
|
39
|
-
setDefaults,
|
40
|
-
setDefaultsFromResource,
|
41
|
-
onConfigUpdated,
|
42
|
-
} from '../lib';
|
43
|
-
|
44
|
-
describe('remoteConfig()', function () {
|
45
|
-
describe('namespace', function () {
|
46
|
-
it('accessible from firebase.app()', function () {
|
47
|
-
const app = firebase.app();
|
48
|
-
expect(app.remoteConfig()).toBeDefined();
|
49
|
-
expect(app.remoteConfig().app).toEqual(app);
|
50
|
-
});
|
51
|
-
|
52
|
-
it('supports multiple apps', async function () {
|
53
|
-
expect(firebase.remoteConfig().app.name).toEqual('[DEFAULT]');
|
54
|
-
expect(firebase.app('secondaryFromNative').remoteConfig().app.name).toEqual(
|
55
|
-
'secondaryFromNative',
|
56
|
-
);
|
57
|
-
});
|
58
|
-
});
|
59
|
-
|
60
|
-
describe('statics', function () {
|
61
|
-
it('LastFetchStatus', function () {
|
62
|
-
expect(firebase.remoteConfig.LastFetchStatus).toBeDefined();
|
63
|
-
expect(firebase.remoteConfig.LastFetchStatus.FAILURE).toEqual('failure');
|
64
|
-
expect(firebase.remoteConfig.LastFetchStatus.SUCCESS).toEqual('success');
|
65
|
-
expect(firebase.remoteConfig.LastFetchStatus.NO_FETCH_YET).toEqual('no_fetch_yet');
|
66
|
-
expect(firebase.remoteConfig.LastFetchStatus.THROTTLED).toEqual('throttled');
|
67
|
-
});
|
68
|
-
|
69
|
-
it('ValueSource', function () {
|
70
|
-
expect(firebase.remoteConfig.ValueSource).toBeDefined();
|
71
|
-
expect(firebase.remoteConfig.ValueSource.REMOTE).toEqual('remote');
|
72
|
-
expect(firebase.remoteConfig.ValueSource.STATIC).toEqual('static');
|
73
|
-
expect(firebase.remoteConfig.ValueSource.DEFAULT).toEqual('default');
|
74
|
-
});
|
75
|
-
});
|
76
|
-
|
77
|
-
describe('fetch()', function () {
|
78
|
-
it('it throws if expiration is not a number', function () {
|
79
|
-
expect(() => {
|
80
|
-
// @ts-ignore - incorrect argument on purpose to check validation
|
81
|
-
firebase.remoteConfig().fetch('foo');
|
82
|
-
}).toThrow('must be a number value');
|
83
|
-
});
|
84
|
-
});
|
85
|
-
|
86
|
-
describe('setConfigSettings()', function () {
|
87
|
-
it('it throws if arg is not an object', async function () {
|
88
|
-
expect(() => {
|
89
|
-
// @ts-ignore - incorrect argument on purpose to check validation
|
90
|
-
firebase.remoteConfig().setConfigSettings('not an object');
|
91
|
-
}).toThrow('must set an object');
|
92
|
-
});
|
93
|
-
|
94
|
-
it('throws if minimumFetchIntervalMillis is not a number', async function () {
|
95
|
-
expect(() => {
|
96
|
-
// @ts-ignore - incorrect argument on purpose to check validation
|
97
|
-
firebase.remoteConfig().setConfigSettings({ minimumFetchIntervalMillis: 'potato' });
|
98
|
-
}).toThrow('must be a number type in milliseconds.');
|
99
|
-
});
|
100
|
-
|
101
|
-
it('throws if fetchTimeMillis is not a number', function () {
|
102
|
-
expect(() => {
|
103
|
-
// @ts-ignore - incorrect argument on purpose to check validation
|
104
|
-
firebase.remoteConfig().setConfigSettings({ fetchTimeMillis: 'potato' });
|
105
|
-
}).toThrow('must be a number type in milliseconds.');
|
106
|
-
});
|
107
|
-
});
|
108
|
-
|
109
|
-
describe('setDefaults()', function () {
|
110
|
-
it('it throws if defaults object not provided', function () {
|
111
|
-
expect(() => {
|
112
|
-
// @ts-ignore - incorrect argument on purpose to check validation
|
113
|
-
firebase.remoteConfig().setDefaults('not an object');
|
114
|
-
}).toThrow('must be an object.');
|
115
|
-
});
|
116
|
-
});
|
117
|
-
|
118
|
-
describe('setDefaultsFromResource()', function () {
|
119
|
-
it('throws if resourceName is not a string', function () {
|
120
|
-
expect(() => {
|
121
|
-
// @ts-ignore - incorrect argument on purpose to check validation
|
122
|
-
firebase.remoteConfig().setDefaultsFromResource(1337);
|
123
|
-
}).toThrow('must be a string value');
|
124
|
-
});
|
125
|
-
});
|
126
|
-
|
127
|
-
describe('getAll() should not crash', function () {
|
128
|
-
it('should return an empty object pre-fetch, pre-defaults', function () {
|
129
|
-
const config = firebase.remoteConfig().getAll();
|
130
|
-
expect(config).toBeDefined();
|
131
|
-
expect(config).toEqual({});
|
132
|
-
});
|
133
|
-
});
|
134
|
-
|
135
|
-
describe('modular', function () {
|
136
|
-
it('`getRemoteConfig` function is properly exposed to end user', function () {
|
137
|
-
expect(getRemoteConfig).toBeDefined();
|
138
|
-
});
|
139
|
-
|
140
|
-
it('`activate` function is properly exposed to end user', function () {
|
141
|
-
expect(activate).toBeDefined();
|
142
|
-
});
|
143
|
-
|
144
|
-
it('`ensureInitialized` function is properly exposed to end user', function () {
|
145
|
-
expect(ensureInitialized).toBeDefined();
|
146
|
-
});
|
147
|
-
|
148
|
-
it('`fetchAndActivate` function is properly exposed to end user', function () {
|
149
|
-
expect(fetchAndActivate).toBeDefined();
|
150
|
-
});
|
151
|
-
|
152
|
-
it('`fetchConfig` function is properly exposed to end user', function () {
|
153
|
-
expect(fetchConfig).toBeDefined();
|
154
|
-
});
|
155
|
-
|
156
|
-
it('`getAll` function is properly exposed to end user', function () {
|
157
|
-
expect(getAll).toBeDefined();
|
158
|
-
});
|
159
|
-
|
160
|
-
it('`getBoolean` function is properly exposed to end user', function () {
|
161
|
-
expect(getBoolean).toBeDefined();
|
162
|
-
});
|
163
|
-
|
164
|
-
it('`getNumber` function is properly exposed to end user', function () {
|
165
|
-
expect(getNumber).toBeDefined();
|
166
|
-
});
|
167
|
-
|
168
|
-
it('`getString` function is properly exposed to end user', function () {
|
169
|
-
expect(getString).toBeDefined();
|
170
|
-
});
|
171
|
-
|
172
|
-
it('`getValue` function is properly exposed to end user', function () {
|
173
|
-
expect(getValue).toBeDefined();
|
174
|
-
});
|
175
|
-
|
176
|
-
it('`setLogLevel` function is properly exposed to end user', function () {
|
177
|
-
expect(setLogLevel).toBeDefined();
|
178
|
-
});
|
179
|
-
|
180
|
-
it('`isSupported` function is properly exposed to end user', function () {
|
181
|
-
expect(isSupported).toBeDefined();
|
182
|
-
});
|
183
|
-
|
184
|
-
it('`fetchTimeMillis` function is properly exposed to end user', function () {
|
185
|
-
expect(fetchTimeMillis).toBeDefined();
|
186
|
-
});
|
187
|
-
|
188
|
-
it('`settings` function is properly exposed to end user', function () {
|
189
|
-
expect(settings).toBeDefined();
|
190
|
-
});
|
191
|
-
|
192
|
-
it('`lastFetchStatus` function is properly exposed to end user', function () {
|
193
|
-
expect(lastFetchStatus).toBeDefined();
|
194
|
-
});
|
195
|
-
|
196
|
-
it('`reset` function is properly exposed to end user', function () {
|
197
|
-
expect(reset).toBeDefined();
|
198
|
-
});
|
199
|
-
|
200
|
-
it('`setConfigSettings` function is properly exposed to end user', function () {
|
201
|
-
expect(setConfigSettings).toBeDefined();
|
202
|
-
});
|
203
|
-
|
204
|
-
it('`fetch` function is properly exposed to end user', function () {
|
205
|
-
expect(fetch).toBeDefined();
|
206
|
-
});
|
207
|
-
|
208
|
-
it('`setDefaults` function is properly exposed to end user', function () {
|
209
|
-
expect(setDefaults).toBeDefined();
|
210
|
-
});
|
211
|
-
|
212
|
-
it('`setDefaultsFromResource` function is properly exposed to end user', function () {
|
213
|
-
expect(setDefaultsFromResource).toBeDefined();
|
214
|
-
});
|
215
|
-
|
216
|
-
it('`onConfigUpdated` function is properly exposed to end user', function () {
|
217
|
-
expect(onConfigUpdated).toBeDefined();
|
218
|
-
});
|
219
|
-
});
|
220
|
-
});
|