From 90bbb972eaea8100b43aebae2d0ff1307f88ffd2 Mon Sep 17 00:00:00 2001 From: Elliot Hesp Date: Tue, 10 Jul 2018 20:11:07 +0100 Subject: [PATCH 01/12] [perf][android] Refactor with Trace/HttpMetric --- .../firebase/perf/RNFirebasePerformance.java | 149 +++++++++++++++++- bridge/android/build.gradle | 4 +- 2 files changed, 148 insertions(+), 5 deletions(-) diff --git a/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java b/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java index 81defc82..da7c071d 100644 --- a/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java +++ b/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java @@ -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 traces = new HashMap<>(); + private HashMap httpMetrics = new HashMap<>(); public RNFirebasePerformance(ReactApplicationContext reactContext) { super(reactContext); @@ -30,19 +36,73 @@ 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 attributes = getOrCreateTrace(identifier).getAttributes(); + WritableMap map = Arguments.createMap(); + + for (Map.Entry 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); + promise.resolve(null); + } + + @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 +110,79 @@ 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 attributes = getOrCreateHttpMetric(url, httpMethod).getAttributes(); + WritableMap map = Arguments.createMap(); + + for (Map.Entry 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); + promise.resolve(null); + } + + @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(); + promise.resolve(null); + } + + /** + * Private + */ + private Trace getOrCreateTrace(String identifier) { if (traces.containsKey(identifier)) { return traces.get(identifier); @@ -58,4 +191,14 @@ 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, httpMethod); + httpMetrics.put(identifier, httpMetric); + return httpMetric; + } } diff --git a/bridge/android/build.gradle b/bridge/android/build.gradle index 8e7ee8c7..f1936428 100755 --- a/bridge/android/build.gradle +++ b/bridge/android/build.gradle @@ -7,9 +7,9 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:3.1.2' + 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' } } From 8f6299f07faa3a73732d2fe62cd9d912dfddcd9e Mon Sep 17 00:00:00 2001 From: Elliot Hesp Date: Tue, 10 Jul 2018 20:12:01 +0100 Subject: [PATCH 02/12] [perf] Break out Trace/HttpMetric classes --- lib/modules/perf/HttpMetric.js | 96 ++++++++++++++++++++++++++++++++++ lib/modules/perf/Trace.js | 57 +++++++++++++++++--- lib/modules/perf/index.js | 23 +++++++- lib/utils/index.js | 1 + 4 files changed, 170 insertions(+), 7 deletions(-) create mode 100644 lib/modules/perf/HttpMetric.js diff --git a/lib/modules/perf/HttpMetric.js b/lib/modules/perf/HttpMetric.js new file mode 100644 index 00000000..cbeeab94 --- /dev/null +++ b/lib/modules/perf/HttpMetric.js @@ -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 { + return getNativeModule(this._perf).getHttpMetricAttribute( + this.url, + this.httpMethod, + attribute + ); + } + + getAttributes(): Promise { + return getNativeModule(this._perf).getHttpMetricAttributes( + this.url, + this.httpMethod + ); + } + + putAttribute(attribute: string, value: string): Promise { + return getNativeModule(this._perf).putHttpMetricAttribute( + this.url, + this.httpMethod, + attribute, + value + ); + } + + removeAttribute(attribute: string): Promise { + return getNativeModule(this._perf).removeHttpMetricAttribute( + this.url, + this.httpMethod, + attribute + ); + } + + setHttpResponseCode(code: number): Promise { + return getNativeModule(this._perf).setHttpMetricResponseCode( + this.url, + this.httpMethod, + code + ); + } + + setRequestPayloadSize(bytes: number): Promise { + return getNativeModule(this._perf).setHttpMetricRequestPayloadSize( + this.url, + this.httpMethod, + bytes + ); + } + + setResponseContentType(type: string): Promise { + return getNativeModule(this._perf).setHttpMetricResponseContentType( + this.url, + this.httpMethod, + type + ); + } + + setResponsePayloadSize(bytes: number): Promise { + return getNativeModule(this._perf).setHttpMetricResponsePayloadSize( + this.url, + this.httpMethod, + bytes + ); + } + + start(): Promise { + return getNativeModule(this._perf).startHttpMetric( + this.url, + this.httpMethod + ); + } + + stop(): Promise { + return getNativeModule(this._perf).stopHttpMetric( + this.url, + this.httpMethod + ); + } +} diff --git a/lib/modules/perf/Trace.js b/lib/modules/perf/Trace.js index 5fd54608..bf784c61 100644 --- a/lib/modules/perf/Trace.js +++ b/lib/modules/perf/Trace.js @@ -14,15 +14,60 @@ export default class Trace { this.identifier = identifier; } - start(): void { - getNativeModule(this._perf).start(this.identifier); + getAttribute(attribute: string): Promise { + return getNativeModule(this._perf).getTraceAttribute( + this.identifier, + attribute + ); } - stop(): void { - getNativeModule(this._perf).stop(this.identifier); + getAttributes(): Promise { + return getNativeModule(this._perf).getTraceAttributes(this.identifier); } - incrementCounter(event: string): void { - getNativeModule(this._perf).incrementCounter(this.identifier, event); + getMetric(metricName: string): Promise { + return getNativeModule(this._perf).getTraceLongMetric( + this.identifier, + metricName + ); + } + + incrementMetric(metricName: string, incrementBy: number): Promise { + return getNativeModule(this._perf).incrementTraceMetric( + this.identifier, + metricName, + incrementBy + ); + } + + putAttribute(attribute: string, value: string): Promise { + return getNativeModule(this._perf).putTraceAttribute( + this.identifier, + attribute, + value + ); + } + + putMetric(metricName: string, value: number): Promise { + return getNativeModule(this._perf).putTraceMetric( + this.identifier, + metricName, + value + ); + } + + removeAttribute(attribute: string): Promise { + return getNativeModule(this._perf).removeTraceAttribute( + this.identifier, + attribute + ); + } + + start(): Promise { + return getNativeModule(this._perf).startTrace(this.identifier); + } + + stop(): Promise { + return getNativeModule(this._perf).stopTrace(this.identifier); } } diff --git a/lib/modules/perf/index.js b/lib/modules/perf/index.js index c22ed35e..92282167 100644 --- a/lib/modules/perf/index.js +++ b/lib/modules/perf/index.js @@ -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'; @@ -27,7 +28,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 +42,22 @@ 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); } + + newHttpMetric(url: string, httpMethod: string) { + if (typeof url !== 'string' || typeof httpMethod !== 'string') { + throw new Error( + 'firebase.perf().newHttpMetric() requires url and httpMethod string values' + ); + } + + return new HttpMetric(this, url, httpMethod); + } } export const statics = {}; diff --git a/lib/utils/index.js b/lib/utils/index.js index 5680419e..6d51b6b9 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -196,6 +196,7 @@ export function noop(): void {} * @returns {*} */ export function stripTrailingSlash(str: string): string { + if (!str || !isString(str)) return str; return str.endsWith('/') ? str.slice(0, -1) : str; } From 417b6ba542dfe3cfaab0919cc475e9462cd7c21c Mon Sep 17 00:00:00 2001 From: Elliot Hesp Date: Tue, 10 Jul 2018 20:14:36 +0100 Subject: [PATCH 03/12] [perf] Update/add perf method tests --- bridge/e2e/perf/httpMetric.e2e.js | 88 ++++++++++++++++++++++++++++ bridge/e2e/perf/perf.e2e.js | 31 ++++++---- bridge/e2e/perf/trace.e2e.js | 96 +++++++++++++++++++++++++------ 3 files changed, 186 insertions(+), 29 deletions(-) create mode 100644 bridge/e2e/perf/httpMetric.e2e.js diff --git a/bridge/e2e/perf/httpMetric.e2e.js b/bridge/e2e/perf/httpMetric.e2e.js new file mode 100644 index 00000000..2be2debd --- /dev/null +++ b/bridge/e2e/perf/httpMetric.e2e.js @@ -0,0 +1,88 @@ +describe('perf()', () => { + describe('HttpMetric', () => { + it('start() & stop()', async () => { + const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET'); + await httpMetric.start(); + 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.stop(); + }); + + xit('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.stop(); + }); + + xit('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.stop(); + }); + + xit('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.stop(); + }); + + xit('removeAttribute()', async () => { + const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET'); + await httpMetric.start(); + await httpMetric.putAttribute('foobar', 'bar'); + const value = await httpMetric.getAttribute('foobar'); + value.should.equal('bar'); + await httpMetric.removeAttribute('foobar'); + const removed = await httpMetric.getAttribute('foobar'); + should.equal(removed, null); + 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(); + }); + }); +}); diff --git a/bridge/e2e/perf/perf.e2e.js b/bridge/e2e/perf/perf.e2e.js index 6d614f1d..20f0f41d 100644 --- a/bridge/e2e/perf/perf.e2e.js +++ b/bridge/e2e/perf/perf.e2e.js @@ -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,23 @@ 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('foo', 'bar'); + 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' + ); }); }); }); diff --git a/bridge/e2e/perf/trace.e2e.js b/bridge/e2e/perf/trace.e2e.js index 0751dcf7..35dedfba 100644 --- a/bridge/e2e/perf/trace.e2e.js +++ b/bridge/e2e/perf/trace.e2e.js @@ -1,28 +1,88 @@ describe('perf()', () => { - describe('Trace', () => { + describe.only('Trace', () => { it('start() & stop()', async () => { const trace = firebase.perf().newTrace('bar'); await trace.start(); 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.shoud.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.only('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(); + }); + }); }); From 2c776a60e8424a1a2f6e80df23052c8eb9b977bb Mon Sep 17 00:00:00 2001 From: Elliot Hesp Date: Thu, 12 Jul 2018 13:25:11 +0100 Subject: [PATCH 04/12] [perf] Return Promise types --- .../firebase/perf/RNFirebasePerformance.java | 62 +++++++++++++++++++ bridge/e2e/perf/perf.e2e.js | 15 +++++ lib/modules/perf/HttpMetric.js | 5 +- lib/modules/perf/Trace.js | 5 +- lib/modules/perf/index.js | 39 +++++++++++- 5 files changed, 121 insertions(+), 5 deletions(-) diff --git a/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java b/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java index da7c071d..1499899c 100644 --- a/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java +++ b/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java @@ -48,6 +48,7 @@ public class RNFirebasePerformance extends ReactContextBaseJavaModule { @ReactMethod public void getTraceAttribute(String identifier, String attribute, Promise promise) { promise.resolve(getOrCreateTrace(identifier).getAttribute(attribute)); +<<<<<<< Updated upstream } @ReactMethod @@ -75,6 +76,35 @@ public class RNFirebasePerformance extends ReactContextBaseJavaModule { } @ReactMethod +======= + } + + @ReactMethod + public void getTraceAttributes(String identifier, Promise promise) { + Map attributes = getOrCreateTrace(identifier).getAttributes(); + WritableMap map = Arguments.createMap(); + + for (Map.Entry 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 +>>>>>>> Stashed changes public void putTraceAttribute(String identifier, String attribute, String value, Promise promise) { getOrCreateTrace(identifier).putAttribute(attribute, value); promise.resolve(null); @@ -197,8 +227,40 @@ public class RNFirebasePerformance extends ReactContextBaseJavaModule { if (httpMetrics.containsKey(identifier)) { return httpMetrics.get(identifier); } +<<<<<<< Updated upstream HttpMetric httpMetric = FirebasePerformance.getInstance().newHttpMetric(url, httpMethod); httpMetrics.put(identifier, httpMetric); return httpMetric; } +======= + 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 ""; + } +>>>>>>> Stashed changes } diff --git a/bridge/e2e/perf/perf.e2e.js b/bridge/e2e/perf/perf.e2e.js index 20f0f41d..2936668b 100644 --- a/bridge/e2e/perf/perf.e2e.js +++ b/bridge/e2e/perf/perf.e2e.js @@ -30,6 +30,7 @@ describe('perf()', () => { describe('newHttpMetric()', () => { it('returns an instance of HttpMetric', async () => { +<<<<<<< Updated upstream const trace = firebase.perf().newHttpMetric('foo', 'bar'); trace.constructor.name.should.be.equal('HttpMetric'); }); @@ -38,6 +39,20 @@ describe('perf()', () => { (() => firebase.perf().newHttpMetric(123, [1, 2])).should.throw( 'firebase.perf().newHttpMetric() requires url and httpMethod string values' ); +======= + const trace = firebase.perf().newHttpMetric('foo', '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 +>>>>>>> Stashed changes }); }); }); diff --git a/lib/modules/perf/HttpMetric.js b/lib/modules/perf/HttpMetric.js index cbeeab94..eb85df2d 100644 --- a/lib/modules/perf/HttpMetric.js +++ b/lib/modules/perf/HttpMetric.js @@ -16,7 +16,7 @@ export default class HttpMetric { this.httpMethod = httpMethod; } - getAttribute(attribute: string): Promise { + getAttribute(attribute: string): Promise { return getNativeModule(this._perf).getHttpMetricAttribute( this.url, this.httpMethod, @@ -31,7 +31,8 @@ export default class HttpMetric { ); } - putAttribute(attribute: string, value: string): Promise { + // TODO return true or false + putAttribute(attribute: string, value: string): Promise { return getNativeModule(this._perf).putHttpMetricAttribute( this.url, this.httpMethod, diff --git a/lib/modules/perf/Trace.js b/lib/modules/perf/Trace.js index bf784c61..174f1bef 100644 --- a/lib/modules/perf/Trace.js +++ b/lib/modules/perf/Trace.js @@ -14,7 +14,7 @@ export default class Trace { this.identifier = identifier; } - getAttribute(attribute: string): Promise { + getAttribute(attribute: string): Promise { return getNativeModule(this._perf).getTraceAttribute( this.identifier, attribute @@ -40,7 +40,8 @@ export default class Trace { ); } - putAttribute(attribute: string, value: string): Promise { + // TODO return true or false + putAttribute(attribute: string, value: string): Promise { return getNativeModule(this._perf).putTraceAttribute( this.identifier, attribute, diff --git a/lib/modules/perf/index.js b/lib/modules/perf/index.js index 92282167..a7a25ad7 100644 --- a/lib/modules/perf/index.js +++ b/lib/modules/perf/index.js @@ -12,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, { @@ -49,13 +72,27 @@ export default class PerformanceMonitoring extends ModuleBase { return new Trace(this, trace); } - newHttpMetric(url: string, httpMethod: string) { + /** + * 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); } } From 2ba6e15ea7a7f76fd18ee05e63eadaabe3f72e9f Mon Sep 17 00:00:00 2001 From: Elliot Hesp Date: Wed, 18 Jul 2018 12:08:04 +0100 Subject: [PATCH 05/12] [perm][ts] Create performance monitoring types --- lib/index.d.ts | 132 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 1 deletion(-) diff --git a/lib/index.d.ts b/lib/index.d.ts index 45c5d54b..0fc88b8b 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -54,7 +54,7 @@ declare module 'react-native-firebase' { RNFirebase.notifications.Notifications, RNFirebase.notifications.NotificationsStatics >; - // perf: FirebaseModuleAndStatics; + perf: FirebaseModuleAndStatics; storage: FirebaseModuleAndStatics; // utils: FirebaseModuleAndStatics; initializeApp(options: Firebase.Options, name: string): App; @@ -1567,6 +1567,136 @@ 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; + + /** + * Return an object of key-value attributes. + */ + getAttributes(): Promise; + + /** + * Get a metric by name. Returns 0 if it does not exist. + */ + getMetric(metricName: string): Promise; + + /** + * Increment a metric by name and value. + */ + incrementMetric(metricName: string, incrementBy: number): Promise; + + /** + * Set an attribute. Returns true if it was set, false if it was not. + */ + putAttribute(attribute: string, value: string): Promise; + + /** + * Set a metric. + */ + putMetric(metricName: string, value: number): Promise; + + /** + * Remove an attribute by name. + */ + removeAttribute(attribute: string): Promise; + + /** + * Start a Trace instance. + */ + start(): Promise; + + /** + * Stop a Trace instance. + */ + stop(): Promise; + } + + interface HttpMetric { + /** + * Return an attribute by name, or null if it does not exist. + */ + getAttribute(attribute: string): Promise; + + /** + * Return an object of key-value attributes. + */ + getAttributes(): Promise + + /** + * Set an attribute. Returns true if it was set, false if it was not. + */ + putAttribute(attribute: string, value: string): Promise; + + /** + * Remove an attribute by name. + */ + removeAttribute(attribute: string): Promise; + + /** + * Set the request HTTP response code. + */ + setHttpResponseCode(code: number): Promise; + + /** + * Set the request payload size, in bytes. + */ + setRequestPayloadSize(bytes: number): Promise; + + /** + * Set the response content type. + */ + setResponseContentType(type: string): Promise; + + /** + * Set the response payload size, in bytes. + */ + setResponsePayloadSize(bytes: number): Promise; + + /** + * Start a HttpMetric instance. + */ + start(): Promise; + + /** + * Stop a HttpMetric instance. + */ + stop(): Promise; + } + } + namespace crashlytics { interface Crashlytics { /** From 4c383d278b55ef2b03ba86695365e6237f2b45b3 Mon Sep 17 00:00:00 2001 From: Elliot Hesp Date: Wed, 18 Jul 2018 12:33:19 +0100 Subject: [PATCH 06/12] [perm][ts] Cleanup --- lib/index.d.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/index.d.ts b/lib/index.d.ts index 0fc88b8b..da0d5068 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -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.Performance; storage(): RNFirebase.storage.Storage; // utils(): RNFirebase.utils.Utils; readonly name: string; @@ -1568,7 +1568,6 @@ declare module 'react-native-firebase' { } namespace perf { - type HttpMethod = | 'CONNECT' | 'DELETE' From 5037248d3b06f70cc44f4d120bd1a523dfdaec41 Mon Sep 17 00:00:00 2001 From: Elliot Hesp Date: Wed, 18 Jul 2018 12:49:12 +0100 Subject: [PATCH 07/12] [perm][android] Cleanup/return boolean values --- .../firebase/perf/RNFirebasePerformance.java | 43 ++----------------- lib/modules/perf/HttpMetric.js | 1 - lib/modules/perf/Trace.js | 1 - 3 files changed, 4 insertions(+), 41 deletions(-) diff --git a/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java b/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java index 1499899c..f73dbdfd 100644 --- a/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java +++ b/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java @@ -48,7 +48,6 @@ public class RNFirebasePerformance extends ReactContextBaseJavaModule { @ReactMethod public void getTraceAttribute(String identifier, String attribute, Promise promise) { promise.resolve(getOrCreateTrace(identifier).getAttribute(attribute)); -<<<<<<< Updated upstream } @ReactMethod @@ -76,38 +75,10 @@ public class RNFirebasePerformance extends ReactContextBaseJavaModule { } @ReactMethod -======= - } - - @ReactMethod - public void getTraceAttributes(String identifier, Promise promise) { - Map attributes = getOrCreateTrace(identifier).getAttributes(); - WritableMap map = Arguments.createMap(); - - for (Map.Entry 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 ->>>>>>> Stashed changes public void putTraceAttribute(String identifier, String attribute, String value, Promise promise) { getOrCreateTrace(identifier).putAttribute(attribute, value); - promise.resolve(null); + // TODO putAttribute returns void? Docs state it returns true/false. + promise.resolve(true); } @ReactMethod @@ -164,7 +135,8 @@ public class RNFirebasePerformance extends ReactContextBaseJavaModule { @ReactMethod public void putHttpMetricAttribute(String url, String httpMethod, String attribute, String value, Promise promise) { getOrCreateHttpMetric(url, httpMethod).putAttribute(attribute, value); - promise.resolve(null); + // TODO putAttribute returns void? Docs state it returns true/false. + promise.resolve(true); } @ReactMethod @@ -227,12 +199,6 @@ public class RNFirebasePerformance extends ReactContextBaseJavaModule { if (httpMetrics.containsKey(identifier)) { return httpMetrics.get(identifier); } -<<<<<<< Updated upstream - HttpMetric httpMetric = FirebasePerformance.getInstance().newHttpMetric(url, httpMethod); - httpMetrics.put(identifier, httpMetric); - return httpMetric; - } -======= HttpMetric httpMetric = FirebasePerformance.getInstance().newHttpMetric(url, this.mapStringToMethod(httpMethod)); httpMetrics.put(identifier, httpMetric); return httpMetric; @@ -262,5 +228,4 @@ public class RNFirebasePerformance extends ReactContextBaseJavaModule { return ""; } ->>>>>>> Stashed changes } diff --git a/lib/modules/perf/HttpMetric.js b/lib/modules/perf/HttpMetric.js index eb85df2d..bccd8df2 100644 --- a/lib/modules/perf/HttpMetric.js +++ b/lib/modules/perf/HttpMetric.js @@ -31,7 +31,6 @@ export default class HttpMetric { ); } - // TODO return true or false putAttribute(attribute: string, value: string): Promise { return getNativeModule(this._perf).putHttpMetricAttribute( this.url, diff --git a/lib/modules/perf/Trace.js b/lib/modules/perf/Trace.js index 174f1bef..a2babf77 100644 --- a/lib/modules/perf/Trace.js +++ b/lib/modules/perf/Trace.js @@ -40,7 +40,6 @@ export default class Trace { ); } - // TODO return true or false putAttribute(attribute: string, value: string): Promise { return getNativeModule(this._perf).putTraceAttribute( this.identifier, From 85f06deac224e51fc7c7ebb5405d4d2e461172c7 Mon Sep 17 00:00:00 2001 From: Elliot Hesp Date: Wed, 18 Jul 2018 12:49:56 +0100 Subject: [PATCH 08/12] [perm][ts] Fix incorrect path --- lib/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/index.d.ts b/lib/index.d.ts index da0d5068..b9eb37e0 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -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; From 6b87ae7056f1c270565ecbd2c4cb47c4e66b9075 Mon Sep 17 00:00:00 2001 From: Elliot Hesp Date: Wed, 18 Jul 2018 13:22:00 +0100 Subject: [PATCH 09/12] [perm][android] Fix tests + remove httpMetric instances on stop() --- .../firebase/perf/RNFirebasePerformance.java | 1 + bridge/e2e/perf/httpMetric.e2e.js | 51 ++++++++++--------- bridge/e2e/perf/perf.e2e.js | 11 ---- bridge/e2e/perf/trace.e2e.js | 4 +- 4 files changed, 31 insertions(+), 36 deletions(-) diff --git a/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java b/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java index f73dbdfd..c0823f59 100644 --- a/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java +++ b/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java @@ -178,6 +178,7 @@ public class RNFirebasePerformance extends ReactContextBaseJavaModule { @ReactMethod public void stopHttpMetric(String url, String httpMethod, Promise promise) { getOrCreateHttpMetric(url, httpMethod).stop(); + httpMetrics.remove(url + httpMethod); promise.resolve(null); } diff --git a/bridge/e2e/perf/httpMetric.e2e.js b/bridge/e2e/perf/httpMetric.e2e.js index 2be2debd..a4968e32 100644 --- a/bridge/e2e/perf/httpMetric.e2e.js +++ b/bridge/e2e/perf/httpMetric.e2e.js @@ -6,33 +6,48 @@ describe('perf()', () => { 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.stop(); - }); - - xit('getAttribute() should return string value', async () => { + 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(); }); - xit('putAttribute()', async () => { + 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(); }); - xit('getAttributes()', async () => { + it('getAttributes()', async () => { const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET'); await httpMetric.start(); await httpMetric.putAttribute('foo', 'bar'); @@ -42,18 +57,8 @@ describe('perf()', () => { foo: 'bar', bar: 'baz', }); - await httpMetric.stop(); - }); - - xit('removeAttribute()', async () => { - const httpMetric = firebase.perf().newHttpMetric('http://foo.com', 'GET'); - await httpMetric.start(); - await httpMetric.putAttribute('foobar', 'bar'); - const value = await httpMetric.getAttribute('foobar'); - value.should.equal('bar'); - await httpMetric.removeAttribute('foobar'); - const removed = await httpMetric.getAttribute('foobar'); - should.equal(removed, null); + await httpMetric.removeAttribute('foo'); + await httpMetric.removeAttribute('bar'); await httpMetric.stop(); }); diff --git a/bridge/e2e/perf/perf.e2e.js b/bridge/e2e/perf/perf.e2e.js index 2936668b..95c84fd2 100644 --- a/bridge/e2e/perf/perf.e2e.js +++ b/bridge/e2e/perf/perf.e2e.js @@ -30,20 +30,10 @@ describe('perf()', () => { describe('newHttpMetric()', () => { it('returns an instance of HttpMetric', async () => { -<<<<<<< Updated upstream const trace = firebase.perf().newHttpMetric('foo', 'bar'); 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' - ); -======= - const trace = firebase.perf().newHttpMetric('foo', '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' @@ -52,7 +42,6 @@ describe('perf()', () => { it('errors if httpMethod not a valid type', async () => { (() => firebase.perf().newHttpMetric('foo', 'FOO')).should.throw(); // TODO error ->>>>>>> Stashed changes }); }); }); diff --git a/bridge/e2e/perf/trace.e2e.js b/bridge/e2e/perf/trace.e2e.js index 35dedfba..00b5432d 100644 --- a/bridge/e2e/perf/trace.e2e.js +++ b/bridge/e2e/perf/trace.e2e.js @@ -1,5 +1,5 @@ describe('perf()', () => { - describe.only('Trace', () => { + describe('Trace', () => { it('start() & stop()', async () => { const trace = firebase.perf().newTrace('bar'); await trace.start(); @@ -74,7 +74,7 @@ describe('perf()', () => { await trace.stop(); }); - it.only('incrementMetric()', async () => { + it('incrementMetric()', async () => { const trace = firebase.perf().newTrace('bar'); await trace.start(); await trace.putMetric('baz', 1); From a0188101244bde558841b1b457e6e14490785e3e Mon Sep 17 00:00:00 2001 From: Elliot Hesp Date: Wed, 18 Jul 2018 13:30:54 +0100 Subject: [PATCH 10/12] [perm][android] 100% JS test coverage --- bridge/e2e/perf/perf.e2e.js | 2 +- bridge/e2e/perf/trace.e2e.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bridge/e2e/perf/perf.e2e.js b/bridge/e2e/perf/perf.e2e.js index 95c84fd2..78df76c4 100644 --- a/bridge/e2e/perf/perf.e2e.js +++ b/bridge/e2e/perf/perf.e2e.js @@ -30,7 +30,7 @@ describe('perf()', () => { describe('newHttpMetric()', () => { it('returns an instance of HttpMetric', async () => { - const trace = firebase.perf().newHttpMetric('foo', 'bar'); + const trace = firebase.perf().newHttpMetric('http://foo.com', 'GET'); trace.constructor.name.should.be.equal('HttpMetric'); }); diff --git a/bridge/e2e/perf/trace.e2e.js b/bridge/e2e/perf/trace.e2e.js index 00b5432d..d9d118e5 100644 --- a/bridge/e2e/perf/trace.e2e.js +++ b/bridge/e2e/perf/trace.e2e.js @@ -28,7 +28,7 @@ describe('perf()', () => { await trace.start(); await trace.putAttribute('foo', 'bar'); const value = await trace.getAttribute('foo'); - value.shoud.equal('bar'); + value.should.equal('bar'); await trace.stop(); }); From 89d8a0a92a440519cf560bcdc0697e56ef3003f8 Mon Sep 17 00:00:00 2001 From: Elliot Hesp Date: Wed, 18 Jul 2018 16:05:59 +0100 Subject: [PATCH 11/12] [perf][ios] Performance ios integration --- ios/RNFirebase/perf/RNFirebasePerformance.h | 1 + ios/RNFirebase/perf/RNFirebasePerformance.m | 232 +++++++++++++++++++- 2 files changed, 223 insertions(+), 10 deletions(-) diff --git a/ios/RNFirebase/perf/RNFirebasePerformance.h b/ios/RNFirebase/perf/RNFirebasePerformance.h index ea65d539..4871b158 100644 --- a/ios/RNFirebase/perf/RNFirebasePerformance.h +++ b/ios/RNFirebase/perf/RNFirebasePerformance.h @@ -10,6 +10,7 @@ } @property NSMutableDictionary *traces; +@property NSMutableDictionary *httpMetrics; @end diff --git a/ios/RNFirebase/perf/RNFirebasePerformance.m b/ios/RNFirebase/perf/RNFirebasePerformance.m index de8e7b9b..f3d4c26f 100644 --- a/ios/RNFirebase/perf/RNFirebasePerformance.m +++ b/ios/RNFirebase/perf/RNFirebasePerformance.m @@ -2,6 +2,7 @@ #if __has_include() #import +#import @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 From 3bee4d53acf55f79faf90254bc50c352a193af9b Mon Sep 17 00:00:00 2001 From: Elliot Hesp Date: Wed, 18 Jul 2018 16:06:52 +0100 Subject: [PATCH 12/12] [perf][android] Check putAttribute was created and return boolean --- .../firebase/perf/RNFirebasePerformance.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java b/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java index c0823f59..38069744 100644 --- a/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java +++ b/android/src/main/java/io/invertase/firebase/perf/RNFirebasePerformance.java @@ -77,8 +77,13 @@ public class RNFirebasePerformance extends ReactContextBaseJavaModule { @ReactMethod public void putTraceAttribute(String identifier, String attribute, String value, Promise promise) { getOrCreateTrace(identifier).putAttribute(attribute, value); - // TODO putAttribute returns void? Docs state it returns true/false. - promise.resolve(true); + // Docs say it returns a bool, actually void so we internally check attributes + Map attributes = getOrCreateTrace(identifier).getAttributes(); + if (attributes.containsKey(attribute)) { + promise.resolve(true); + } else { + promise.resolve(false); + } } @ReactMethod @@ -135,8 +140,13 @@ public class RNFirebasePerformance extends ReactContextBaseJavaModule { @ReactMethod public void putHttpMetricAttribute(String url, String httpMethod, String attribute, String value, Promise promise) { getOrCreateHttpMetric(url, httpMethod).putAttribute(attribute, value); - // TODO putAttribute returns void? Docs state it returns true/false. - promise.resolve(true); + // Docs say it returns a bool, actually void so we internally check attributes + Map attributes = getOrCreateHttpMetric(url, httpMethod).getAttributes(); + if (attributes.containsKey(attribute)) { + promise.resolve( true); + } else { + promise.resolve(false); + } } @ReactMethod