2
0
mirror of synced 2025-01-11 14:44:12 +00:00

Merge pull request #1316 from invertase/perf

Performance Monitoring upgrade
This commit is contained in:
Michael Diarmid 2018-08-04 22:00:20 +01:00 committed by GitHub
commit 1d185c802e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 939 additions and 51 deletions

View File

@ -3,18 +3,24 @@ package io.invertase.firebase.perf;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import com.google.firebase.perf.FirebasePerformance;
import com.google.firebase.perf.metrics.Trace;
import com.google.firebase.perf.metrics.HttpMetric;
import java.util.HashMap;
import java.util.Map;
public class RNFirebasePerformance extends ReactContextBaseJavaModule {
private static final String TAG = "RNFirebasePerformance";
private HashMap<String, Trace> traces = new HashMap<>();
private HashMap<String, HttpMetric> httpMetrics = new HashMap<>();
public RNFirebasePerformance(ReactApplicationContext reactContext) {
super(reactContext);
@ -30,19 +36,79 @@ public class RNFirebasePerformance extends ReactContextBaseJavaModule {
}
@ReactMethod
public void setPerformanceCollectionEnabled(Boolean enabled) {
public void setPerformanceCollectionEnabled(Boolean enabled, Promise promise) {
FirebasePerformance.getInstance().setPerformanceCollectionEnabled(enabled);
promise.resolve(null);
}
/**
* Trace
*/
@ReactMethod
public void getTraceAttribute(String identifier, String attribute, Promise promise) {
promise.resolve(getOrCreateTrace(identifier).getAttribute(attribute));
}
@ReactMethod
public void start(String identifier) {
public void getTraceAttributes(String identifier, Promise promise) {
Map<String, String> attributes = getOrCreateTrace(identifier).getAttributes();
WritableMap map = Arguments.createMap();
for (Map.Entry<String, String> entry : attributes.entrySet()) {
map.putString(entry.getKey(), entry.getValue());
}
promise.resolve(map);
}
@ReactMethod
public void getTraceLongMetric(String identifier, String metricName, Promise promise) {
Integer value = Long.valueOf(getOrCreateTrace(identifier).getLongMetric(metricName)).intValue();
promise.resolve(value);
}
@ReactMethod
public void incrementTraceMetric(String identifier, String metricName, Integer incrementBy, Promise promise) {
getOrCreateTrace(identifier).incrementMetric(metricName, incrementBy.longValue());
promise.resolve(null);
}
@ReactMethod
public void putTraceAttribute(String identifier, String attribute, String value, Promise promise) {
getOrCreateTrace(identifier).putAttribute(attribute, value);
// Docs say it returns a bool, actually void so we internally check attributes
Map<String, String> attributes = getOrCreateTrace(identifier).getAttributes();
if (attributes.containsKey(attribute)) {
promise.resolve(true);
} else {
promise.resolve(false);
}
}
@ReactMethod
public void putTraceMetric(String identifier, String metricName, Integer value, Promise promise) {
getOrCreateTrace(identifier).putMetric(metricName, value.longValue());
promise.resolve(null);
}
@ReactMethod
public void removeTraceAttribute(String identifier, String attribute, Promise promise) {
getOrCreateTrace(identifier).removeAttribute(attribute);
promise.resolve(null);
}
@ReactMethod
public void startTrace(String identifier, Promise promise) {
getOrCreateTrace(identifier).start();
promise.resolve(null);
}
@ReactMethod
public void stop(String identifier) {
public void stopTrace(String identifier, Promise promise) {
getOrCreateTrace(identifier).stop();
traces.remove(identifier);
promise.resolve(null);
}
@ReactMethod
@ -50,6 +116,86 @@ public class RNFirebasePerformance extends ReactContextBaseJavaModule {
getOrCreateTrace(identifier).incrementCounter(event);
}
/**
* Http Metric
*/
@ReactMethod
public void getHttpMetricAttribute(String url, String httpMethod, String attribute, Promise promise) {
promise.resolve(getOrCreateHttpMetric(url, httpMethod).getAttribute(attribute));
}
@ReactMethod
public void getHttpMetricAttributes(String url, String httpMethod, Promise promise) {
Map<String, String> attributes = getOrCreateHttpMetric(url, httpMethod).getAttributes();
WritableMap map = Arguments.createMap();
for (Map.Entry<String, String> entry : attributes.entrySet()) {
map.putString(entry.getKey(), entry.getValue());
}
promise.resolve(map);
}
@ReactMethod
public void putHttpMetricAttribute(String url, String httpMethod, String attribute, String value, Promise promise) {
getOrCreateHttpMetric(url, httpMethod).putAttribute(attribute, value);
// Docs say it returns a bool, actually void so we internally check attributes
Map<String, String> attributes = getOrCreateHttpMetric(url, httpMethod).getAttributes();
if (attributes.containsKey(attribute)) {
promise.resolve( true);
} else {
promise.resolve(false);
}
}
@ReactMethod
public void removeHttpMetricAttribute(String url, String httpMethod, String attribute, Promise promise) {
getOrCreateHttpMetric(url, httpMethod).removeAttribute(attribute);
promise.resolve(null);
}
@ReactMethod
public void setHttpMetricResponseCode(String url, String httpMethod, Integer code, Promise promise) {
getOrCreateHttpMetric(url, httpMethod).setHttpResponseCode(code);
promise.resolve(null);
}
@ReactMethod
public void setHttpMetricRequestPayloadSize(String url, String httpMethod, Integer bytes, Promise promise) {
getOrCreateHttpMetric(url, httpMethod).setRequestPayloadSize(bytes.longValue());
promise.resolve(null);
}
@ReactMethod
public void setHttpMetricResponseContentType(String url, String httpMethod, String type, Promise promise) {
getOrCreateHttpMetric(url, httpMethod).setResponseContentType(type);
promise.resolve(null);
}
@ReactMethod
public void setHttpMetricResponsePayloadSize(String url, String httpMethod, Integer bytes, Promise promise) {
getOrCreateHttpMetric(url, httpMethod).setResponsePayloadSize(bytes.longValue());
promise.resolve(null);
}
@ReactMethod
public void startHttpMetric(String url, String httpMethod, Promise promise) {
getOrCreateHttpMetric(url, httpMethod).start();
promise.resolve(null);
}
@ReactMethod
public void stopHttpMetric(String url, String httpMethod, Promise promise) {
getOrCreateHttpMetric(url, httpMethod).stop();
httpMetrics.remove(url + httpMethod);
promise.resolve(null);
}
/**
* Private
*/
private Trace getOrCreateTrace(String identifier) {
if (traces.containsKey(identifier)) {
return traces.get(identifier);
@ -58,4 +204,39 @@ public class RNFirebasePerformance extends ReactContextBaseJavaModule {
traces.put(identifier, trace);
return trace;
}
private HttpMetric getOrCreateHttpMetric(String url, String httpMethod) {
String identifier = url + httpMethod;
if (httpMetrics.containsKey(identifier)) {
return httpMetrics.get(identifier);
}
HttpMetric httpMetric = FirebasePerformance.getInstance().newHttpMetric(url, this.mapStringToMethod(httpMethod));
httpMetrics.put(identifier, httpMetric);
return httpMetric;
}
private String mapStringToMethod(String value) {
switch (value) {
case "CONNECT":
return FirebasePerformance.HttpMethod.CONNECT;
case "DELETE":
return FirebasePerformance.HttpMethod.DELETE;
case "GET":
return FirebasePerformance.HttpMethod.GET;
case "HEAD":
return FirebasePerformance.HttpMethod.HEAD;
case "OPTIONS":
return FirebasePerformance.HttpMethod.OPTIONS;
case "PATCH":
return FirebasePerformance.HttpMethod.PATCH;
case "POST":
return FirebasePerformance.HttpMethod.POST;
case "PUT":
return FirebasePerformance.HttpMethod.PUT;
case "TRACE":
return FirebasePerformance.HttpMethod.TRACE;
}
return "";
}
}

View File

@ -9,7 +9,7 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:3.1.3'
classpath 'com.google.gms:google-services:4.0.1'
classpath 'com.google.firebase:firebase-plugins:1.1.1'
classpath 'com.google.firebase:firebase-plugins:1.1.5'
classpath 'io.fabric.tools:gradle:1.25.4'
}
}

View File

@ -0,0 +1,93 @@
describe('perf()', () => {
describe('HttpMetric', () => {
it('start() & stop()', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
await httpMetric.stop();
});
it('removeAttribute()', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
await httpMetric.putAttribute('foo', 'bar');
const value = await httpMetric.getAttribute('foo');
should.equal(value, 'bar');
await httpMetric.removeAttribute('foo');
const value2 = await httpMetric.getAttribute('foo');
should.equal(value2, null);
await httpMetric.stop();
});
it('getAttribute() should return null', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
const value = await httpMetric.getAttribute('foo');
should.equal(value, null);
await httpMetric.removeAttribute('foo');
await httpMetric.stop();
});
it('getAttribute() should return string value', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
await httpMetric.putAttribute('foo', 'bar');
const value = await httpMetric.getAttribute('foo');
should.equal(value, 'bar');
await httpMetric.removeAttribute('foo');
await httpMetric.stop();
});
it('putAttribute()', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
await httpMetric.putAttribute('foo', 'bar');
const value = await httpMetric.getAttribute('foo');
value.should.equal('bar');
await httpMetric.removeAttribute('foo');
await httpMetric.stop();
});
it('getAttributes()', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
await httpMetric.putAttribute('foo', 'bar');
await httpMetric.putAttribute('bar', 'baz');
const value = await httpMetric.getAttributes();
value.should.deepEqual({
foo: 'bar',
bar: 'baz',
});
await httpMetric.removeAttribute('foo');
await httpMetric.removeAttribute('bar');
await httpMetric.stop();
});
it('setHttpResponseCode()', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
await httpMetric.setHttpResponseCode(500);
await httpMetric.stop();
});
it('setRequestPayloadSize()', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
await httpMetric.setRequestPayloadSize(1234567);
await httpMetric.stop();
});
it('setResponseContentType()', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
await httpMetric.setResponseContentType('application/foobar');
await httpMetric.stop();
});
it('setResponsePayloadSize()', async () => {
const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET');
await httpMetric.start();
await httpMetric.setResponsePayloadSize(123456789);
await httpMetric.stop();
});
});
});

View File

@ -8,9 +8,10 @@ describe('perf()', () => {
await firebase.perf().setPerformanceCollectionEnabled(false);
});
xit('errors if not boolean', async () => {
// TODO add validations to lib
await firebase.perf().setPerformanceCollectionEnabled();
it('errors if not boolean', async () => {
(() => firebase.perf().setPerformanceCollectionEnabled()).should.throw(
'firebase.perf().setPerformanceCollectionEnabled() requires a boolean value'
);
});
});
@ -20,15 +21,27 @@ describe('perf()', () => {
trace.constructor.name.should.be.equal('Trace');
});
xit('errors if identifier not a string', async () => {
// TODO add validations to lib
try {
firebase.perf().newTrace([1, 2, 3, 4]);
} catch (e) {
return undefined;
}
it('errors if identifier not a string', async () => {
(() => firebase.perf().newTrace([1, 2, 3, 4])).should.throw(
'firebase.perf().newTrace() requires a string value'
);
});
});
throw new Error('Trace did not error on invalid identifier');
describe('newHttpMetric()', () => {
it('returns an instance of HttpMetric', async () => {
const trace = firebase.perf().newHttpMetric('http://foo.com', 'GET');
trace.constructor.name.should.be.equal('HttpMetric');
});
it('errors if url/httpMethod not a string', async () => {
(() => firebase.perf().newHttpMetric(123, [1, 2])).should.throw(
'firebase.perf().newHttpMetric() requires url and httpMethod string values'
);
});
it('errors if httpMethod not a valid type', async () => {
(() => firebase.perf().newHttpMetric('foo', 'FOO')).should.throw(); // TODO error
});
});
});

View File

@ -6,23 +6,83 @@ describe('perf()', () => {
await trace.stop();
});
describe('incrementCounter()', () => {
it('accepts a string event', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
await trace.incrementCounter('fooby');
await trace.incrementCounter('fooby');
await trace.incrementCounter('fooby');
await trace.incrementCounter('fooby');
await trace.stop();
});
xit('errors if event is not a string', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
await trace.incrementCounter([1, 2, 3, 4]);
await trace.stop();
});
it('getAttribute() should return null', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
const value = await trace.getAttribute('foo');
should.equal(value, null);
await trace.stop();
});
it('getAttribute() should return string value', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
await trace.putAttribute('foo', 'bar');
const value = await trace.getAttribute('foo');
should.equal(value, 'bar');
await trace.stop();
});
it('putAttribute()', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
await trace.putAttribute('foo', 'bar');
const value = await trace.getAttribute('foo');
value.should.equal('bar');
await trace.stop();
});
it('getAttributes()', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
await trace.putAttribute('foo', 'bar');
await trace.putAttribute('bar', 'baz');
const value = await trace.getAttributes();
value.should.deepEqual({
foo: 'bar',
bar: 'baz',
});
await trace.stop();
});
it('removeAttribute()', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
await trace.putAttribute('foobar', 'bar');
const value = await trace.getAttribute('foobar');
value.should.equal('bar');
await trace.removeAttribute('foobar');
const removed = await trace.getAttribute('foobar');
should.equal(removed, null);
await trace.stop();
});
it('getMetric()', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
const metric = await trace.getMetric('foo');
metric.should.equal(0);
await trace.stop();
});
it('putMetric()', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
await trace.putMetric('baz', 1);
const metric = await trace.getMetric('baz');
metric.should.equal(1);
await trace.stop();
});
it('incrementMetric()', async () => {
const trace = firebase.perf().newTrace('bar');
await trace.start();
await trace.putMetric('baz', 1);
await trace.incrementMetric('baz', 2);
const metric = await trace.getMetric('baz');
metric.should.equal(3);
await trace.stop();
});
});
});

View File

@ -10,6 +10,7 @@
}
@property NSMutableDictionary *traces;
@property NSMutableDictionary *httpMetrics;
@end

View File

@ -2,6 +2,7 @@
#if __has_include(<FirebasePerformance/FIRPerformance.h>)
#import <FirebasePerformance/FIRPerformance.h>
#import <FirebasePerformance/FIRHTTPMetric.h>
@implementation RNFirebasePerformance
RCT_EXPORT_MODULE();
@ -9,10 +10,25 @@ RCT_EXPORT_MODULE();
self = [super init];
if (self != nil) {
_traces = [[NSMutableDictionary alloc] init];
_httpMetrics = [[NSMutableDictionary alloc] init];
}
return self;
}
- (FIRHTTPMethod) mapStringToMethod:(NSString *) value {
if ([value compare:@"get" options:NSCaseInsensitiveSearch] == NSOrderedSame) return FIRHTTPMethodGET;
if ([value compare:@"put" options:NSCaseInsensitiveSearch] == NSOrderedSame) return FIRHTTPMethodPUT;
if ([value compare:@"post" options:NSCaseInsensitiveSearch] == NSOrderedSame) return FIRHTTPMethodPUT;
if ([value compare:@"delete" options:NSCaseInsensitiveSearch] == NSOrderedSame) return FIRHTTPMethodDELETE;
if ([value compare:@"head" options:NSCaseInsensitiveSearch] == NSOrderedSame) return FIRHTTPMethodHEAD;
if ([value compare:@"patch" options:NSCaseInsensitiveSearch] == NSOrderedSame) return FIRHTTPMethodPATCH;
if ([value compare:@"options" options:NSCaseInsensitiveSearch] == NSOrderedSame) return FIRHTTPMethodOPTIONS;
if ([value compare:@"trace" options:NSCaseInsensitiveSearch] == NSOrderedSame) return FIRHTTPMethodTRACE;
if ([value compare:@"connect" options:NSCaseInsensitiveSearch] == NSOrderedSame) return FIRHTTPMethodCONNECT;
return FIRHTTPMethodGET;
}
- (FIRTrace *)getOrCreateTrace:(NSString *)identifier {
if (_traces[identifier]) {
return _traces[identifier];
@ -22,27 +38,223 @@ RCT_EXPORT_MODULE();
return trace;
}
- (FIRHTTPMetric *)getOrCreateHttpMetric:(NSString *)url httpMethod:(NSString *) httpMethod {
NSString *identifier = [NSString stringWithFormat:@"%@%@", url, httpMethod];
if (_httpMetrics[identifier]) {
return _httpMetrics[identifier];
}
NSURL * toURL = [NSURL URLWithString:url];
FIRHTTPMethod method = [self mapStringToMethod:httpMethod];
FIRHTTPMetric *httpMetric = [[FIRHTTPMetric alloc] initWithURL:toURL HTTPMethod:method];
_httpMetrics[identifier] = httpMetric;
return httpMetric;
}
RCT_EXPORT_METHOD(setPerformanceCollectionEnabled:
(BOOL *) enabled) {
[FIRPerformance sharedInstance].dataCollectionEnabled = (BOOL) enabled;
}
RCT_EXPORT_METHOD(start:
(NSString *) identifier) {
/**
* Trace
*/
RCT_EXPORT_METHOD(startTrace:
(NSString *) identifier
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
[[self getOrCreateTrace:identifier] start];
resolve([NSNull null]);
}
RCT_EXPORT_METHOD(stop:
(NSString *) identifier) {
RCT_EXPORT_METHOD(stopTrace:
(NSString *) identifier
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
[[self getOrCreateTrace:identifier] stop];
_traces[identifier] = nil;
[_traces removeObjectForKey:identifier];
resolve([NSNull null]);
}
RCT_EXPORT_METHOD(incrementCounter:
(NSString *) identifier
event:
(NSString *) event) {
[[self getOrCreateTrace:identifier] incrementCounterNamed:event];
RCT_EXPORT_METHOD(getTraceAttribute:
(NSString *) identifier
attribute:(NSString *) attribute
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
NSString *value = [[self getOrCreateTrace:identifier] valueForAttribute:attribute];
resolve(value ? value : [NSNull null]);
}
RCT_EXPORT_METHOD(getTraceAttributes:
(NSString *) identifier
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
resolve([[self getOrCreateTrace:identifier] attributes]);
}
RCT_EXPORT_METHOD(getTraceLongMetric:
(NSString *) identifier
metricName:(NSString *) metricName
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
int64_t value = [[self getOrCreateTrace:identifier] valueForIntMetric:metricName];
resolve(@(value));
}
RCT_EXPORT_METHOD(incrementTraceMetric:
(NSString *) identifier
metricName:(NSString *) metricName
incrementBy:(NSNumber *) incrementBy
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
int64_t byInt = [incrementBy intValue];
[[self getOrCreateTrace:identifier] incrementMetric:metricName byInt:byInt];
resolve([NSNull null]);
}
RCT_EXPORT_METHOD(putTraceAttribute:
(NSString *) identifier
attribute:(NSString *) attribute
value:(NSString *) value
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
FIRTrace * trace = [self getOrCreateTrace:identifier];
[trace setValue:value forAttribute:attribute];
if (trace.attributes[attribute] != nil) {
resolve(@(YES));
} else {
resolve(@(NO));
}
}
RCT_EXPORT_METHOD(putTraceMetric:
(NSString *) identifier
attribute:(NSString *) attribute
value:(NSNumber *) value
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
int64_t byInt = [value intValue];
[[self getOrCreateTrace:identifier] setIntValue:byInt forMetric:attribute];
resolve([NSNull null]);
}
RCT_EXPORT_METHOD(removeTraceAttribute:
(NSString *) identifier
attribute:(NSString *) attribute
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
[[self getOrCreateTrace:identifier] removeAttribute:attribute];
resolve([NSNull null]);
}
/**
* HTTP Metric
*/
RCT_EXPORT_METHOD(startHttpMetric:
(NSString *) url
httpMethod:(NSString *) httpMethod
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
[[self getOrCreateHttpMetric:url httpMethod:httpMethod] start];
resolve([NSNull null]);
}
RCT_EXPORT_METHOD(stopHttpMetric:
(NSString *) url
httpMethod:(NSString *) httpMethod
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
[[self getOrCreateHttpMetric:url httpMethod:httpMethod] stop];
[_httpMetrics removeObjectForKey:[NSString stringWithFormat:@"%@%@", url, httpMethod]];
resolve([NSNull null]);
}
RCT_EXPORT_METHOD(getHttpMetricAttribute:
(NSString *) url
httpMethod:(NSString *) httpMethod
attribute:(NSString *) attribute
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
NSString *value = [[self getOrCreateHttpMetric:url httpMethod:httpMethod] valueForAttribute:attribute];
resolve(value ? value : [NSNull null]);
}
RCT_EXPORT_METHOD(getHttpMetricAttributes:
(NSString *) url
httpMethod:(NSString *) httpMethod
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
resolve([[self getOrCreateHttpMetric:url httpMethod:httpMethod] attributes]);
}
RCT_EXPORT_METHOD(putHttpMetricAttribute:
(NSString *) url
httpMethod:(NSString *) httpMethod
attribute:(NSString *) attribute
value:(NSString *) value
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
FIRHTTPMetric * httpMetric = [self getOrCreateHttpMetric:url httpMethod:httpMethod];
[httpMetric setValue:value forAttribute:attribute];
if (httpMetric.attributes[attribute] != nil) {
resolve(@(YES));
} else {
resolve(@(NO));
}
}
RCT_EXPORT_METHOD(removeHttpMetricAttribute:
(NSString *) url
httpMethod:(NSString *) httpMethod
attribute:(NSString *) attribute
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
[[self getOrCreateHttpMetric:url httpMethod:httpMethod] removeAttribute:attribute];
resolve([NSNull null]);
}
RCT_EXPORT_METHOD(setHttpMetricResponseCode:
(NSString *) url
httpMethod:(NSString *) httpMethod
code:(NSNumber *) code
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
[[self getOrCreateHttpMetric:url httpMethod:httpMethod] setResponseCode:[code integerValue]];
resolve([NSNull null]);
}
RCT_EXPORT_METHOD(setHttpMetricRequestPayloadSize:
(NSString *) url
httpMethod:(NSString *) httpMethod
bytes:(NSNumber *) bytes
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
[[self getOrCreateHttpMetric:url httpMethod:httpMethod] setRequestPayloadSize:[bytes longLongValue]];
resolve([NSNull null]);
}
RCT_EXPORT_METHOD(setHttpMetricResponseContentType:
(NSString *) url
httpMethod:(NSString *) httpMethod
type:(NSString *) type
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
[[self getOrCreateHttpMetric:url httpMethod:httpMethod] setResponseContentType:type];
resolve([NSNull null]);
}
RCT_EXPORT_METHOD(setHttpMetricResponsePayloadSize:
(NSString *) url
httpMethod:(NSString *) httpMethod
bytes:(NSNumber *) bytes
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
[[self getOrCreateHttpMetric:url httpMethod:httpMethod] setResponsePayloadSize:[bytes longLongValue]];
resolve([NSNull null]);
}
+ (BOOL)requiresMainQueueSetup

133
lib/index.d.ts vendored
View File

@ -54,7 +54,7 @@ declare module 'react-native-firebase' {
RNFirebase.notifications.Notifications,
RNFirebase.notifications.NotificationsStatics
>;
// perf: FirebaseModuleAndStatics<RNFirebase.perf.Perf>;
perf: FirebaseModuleAndStatics<RNFirebase.perf.Perf>;
storage: FirebaseModuleAndStatics<RNFirebase.storage.Storage>;
// utils: FirebaseModuleAndStatics<RNFirebase.utils.Utils>;
initializeApp(options: Firebase.Options, name: string): App;
@ -92,7 +92,7 @@ declare module 'react-native-firebase' {
links(): RNFirebase.links.Links;
messaging(): RNFirebase.messaging.Messaging;
notifications(): RNFirebase.notifications.Notifications;
// perf(): RNFirebase.perf.Performance;
perf(): RNFirebase.perf.Perf;
storage(): RNFirebase.storage.Storage;
// utils(): RNFirebase.utils.Utils;
readonly name: string;
@ -1569,6 +1569,135 @@ declare module 'react-native-firebase' {
}
}
namespace perf {
type HttpMethod =
| 'CONNECT'
| 'DELETE'
| 'GET'
| 'HEAD'
| 'OPTIONS'
| 'PATCH'
| 'POST'
| 'PUT'
| 'TRACE';
interface Perf {
/**
* Globally enable or disable performance monitoring.
*/
setPerformanceCollectionEnabled(enabled: boolean): void;
/**
* Returns a new Trace instance.
*/
newTrace(trace: string): Trace;
/**
* Returns a new HTTP Metric instance.
*/
newHttpMetric(url: string, httpMethod: HttpMethod): HttpMetric;
}
interface Trace {
/**
* Return an attribute by name, or null if it does not exist.
*/
getAttribute(attribute: string): Promise<string | null>;
/**
* Return an object of key-value attributes.
*/
getAttributes(): Promise<Object>;
/**
* Get a metric by name. Returns 0 if it does not exist.
*/
getMetric(metricName: string): Promise<number>;
/**
* Increment a metric by name and value.
*/
incrementMetric(metricName: string, incrementBy: number): Promise<null>;
/**
* Set an attribute. Returns true if it was set, false if it was not.
*/
putAttribute(attribute: string, value: string): Promise<true | false>;
/**
* Set a metric.
*/
putMetric(metricName: string, value: number): Promise<null>;
/**
* Remove an attribute by name.
*/
removeAttribute(attribute: string): Promise<null>;
/**
* Start a Trace instance.
*/
start(): Promise<null>;
/**
* Stop a Trace instance.
*/
stop(): Promise<null>;
}
interface HttpMetric {
/**
* Return an attribute by name, or null if it does not exist.
*/
getAttribute(attribute: string): Promise<string | null>;
/**
* Return an object of key-value attributes.
*/
getAttributes(): Promise<Object>
/**
* Set an attribute. Returns true if it was set, false if it was not.
*/
putAttribute(attribute: string, value: string): Promise<true | false>;
/**
* Remove an attribute by name.
*/
removeAttribute(attribute: string): Promise<null>;
/**
* Set the request HTTP response code.
*/
setHttpResponseCode(code: number): Promise<null>;
/**
* Set the request payload size, in bytes.
*/
setRequestPayloadSize(bytes: number): Promise<null>;
/**
* Set the response content type.
*/
setResponseContentType(type: string): Promise<null>;
/**
* Set the response payload size, in bytes.
*/
setResponsePayloadSize(bytes: number): Promise<null>;
/**
* Start a HttpMetric instance.
*/
start(): Promise<null>;
/**
* Stop a HttpMetric instance.
*/
stop(): Promise<null>;
}
}
namespace crashlytics {
interface Crashlytics {
/**

View File

@ -0,0 +1,96 @@
/**
* @flow
* Trace representation wrapper
*/
import { getNativeModule } from '../../utils/native';
import type PerformanceMonitoring from './';
export default class HttpMetric {
url: string;
httpMethod: string;
_perf: PerformanceMonitoring;
constructor(perf: PerformanceMonitoring, url: string, httpMethod: string) {
this._perf = perf;
this.url = url;
this.httpMethod = httpMethod;
}
getAttribute(attribute: string): Promise<string | null> {
return getNativeModule(this._perf).getHttpMetricAttribute(
this.url,
this.httpMethod,
attribute
);
}
getAttributes(): Promise<Object> {
return getNativeModule(this._perf).getHttpMetricAttributes(
this.url,
this.httpMethod
);
}
putAttribute(attribute: string, value: string): Promise<true | false> {
return getNativeModule(this._perf).putHttpMetricAttribute(
this.url,
this.httpMethod,
attribute,
value
);
}
removeAttribute(attribute: string): Promise<null> {
return getNativeModule(this._perf).removeHttpMetricAttribute(
this.url,
this.httpMethod,
attribute
);
}
setHttpResponseCode(code: number): Promise<null> {
return getNativeModule(this._perf).setHttpMetricResponseCode(
this.url,
this.httpMethod,
code
);
}
setRequestPayloadSize(bytes: number): Promise<null> {
return getNativeModule(this._perf).setHttpMetricRequestPayloadSize(
this.url,
this.httpMethod,
bytes
);
}
setResponseContentType(type: string): Promise<null> {
return getNativeModule(this._perf).setHttpMetricResponseContentType(
this.url,
this.httpMethod,
type
);
}
setResponsePayloadSize(bytes: number): Promise<null> {
return getNativeModule(this._perf).setHttpMetricResponsePayloadSize(
this.url,
this.httpMethod,
bytes
);
}
start(): Promise<null> {
return getNativeModule(this._perf).startHttpMetric(
this.url,
this.httpMethod
);
}
stop(): Promise<null> {
return getNativeModule(this._perf).stopHttpMetric(
this.url,
this.httpMethod
);
}
}

View File

@ -14,15 +14,60 @@ export default class Trace {
this.identifier = identifier;
}
start(): void {
getNativeModule(this._perf).start(this.identifier);
getAttribute(attribute: string): Promise<string | null> {
return getNativeModule(this._perf).getTraceAttribute(
this.identifier,
attribute
);
}
stop(): void {
getNativeModule(this._perf).stop(this.identifier);
getAttributes(): Promise<Object> {
return getNativeModule(this._perf).getTraceAttributes(this.identifier);
}
incrementCounter(event: string): void {
getNativeModule(this._perf).incrementCounter(this.identifier, event);
getMetric(metricName: string): Promise<number> {
return getNativeModule(this._perf).getTraceLongMetric(
this.identifier,
metricName
);
}
incrementMetric(metricName: string, incrementBy: number): Promise<null> {
return getNativeModule(this._perf).incrementTraceMetric(
this.identifier,
metricName,
incrementBy
);
}
putAttribute(attribute: string, value: string): Promise<true | false> {
return getNativeModule(this._perf).putTraceAttribute(
this.identifier,
attribute,
value
);
}
putMetric(metricName: string, value: number): Promise<null> {
return getNativeModule(this._perf).putTraceMetric(
this.identifier,
metricName,
value
);
}
removeAttribute(attribute: string): Promise<null> {
return getNativeModule(this._perf).removeTraceAttribute(
this.identifier,
attribute
);
}
start(): Promise<null> {
return getNativeModule(this._perf).startTrace(this.identifier);
}
stop(): Promise<null> {
return getNativeModule(this._perf).stopTrace(this.identifier);
}
}

View File

@ -3,6 +3,7 @@
* Performance monitoring representation wrapper
*/
import Trace from './Trace';
import HttpMetric from './HttpMetric';
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
@ -11,6 +12,29 @@ import type App from '../core/app';
export const MODULE_NAME = 'RNFirebasePerformance';
export const NAMESPACE = 'perf';
const HTTP_METHODS = {
CONNECT: true,
DELETE: true,
GET: true,
HEAD: true,
OPTIONS: true,
PATCH: true,
POST: true,
PUT: true,
TRACE: true,
};
type HttpMethod =
| 'CONNECT'
| 'DELETE'
| 'GET'
| 'HEAD'
| 'OPTIONS'
| 'PATCH'
| 'POST'
| 'PUT'
| 'TRACE';
export default class PerformanceMonitoring extends ModuleBase {
constructor(app: App) {
super(app, {
@ -27,7 +51,13 @@ export default class PerformanceMonitoring extends ModuleBase {
* @returns {*}
*/
setPerformanceCollectionEnabled(enabled: boolean): void {
getNativeModule(this).setPerformanceCollectionEnabled(enabled);
if (typeof enabled !== 'boolean') {
throw new Error(
'firebase.perf().setPerformanceCollectionEnabled() requires a boolean value'
);
}
return getNativeModule(this).setPerformanceCollectionEnabled(enabled);
}
/**
@ -35,8 +65,36 @@ export default class PerformanceMonitoring extends ModuleBase {
* @param trace
*/
newTrace(trace: string): Trace {
if (typeof trace !== 'string') {
throw new Error('firebase.perf().newTrace() requires a string value');
}
return new Trace(this, trace);
}
/**
* Return a new HttpMetric instance
* @param url
* @param httpMethod
* @returns {HttpMetric}
*/
newHttpMetric(url: string, httpMethod: HttpMethod): HttpMetric {
if (typeof url !== 'string' || typeof httpMethod !== 'string') {
throw new Error(
'firebase.perf().newHttpMetric() requires url and httpMethod string values'
);
}
if (!HTTP_METHODS[httpMethod]) {
throw new Error(
`firebase.perf().newHttpMetric() httpMethod should be one of ${Object.keys(
HTTP_METHODS
).join(', ')}`
);
}
return new HttpMetric(this, url, httpMethod);
}
}
export const statics = {};