diff --git a/packages/performance/README.md b/packages/performance/README.md index 1e5f9e2b..7a56804b 100644 --- a/packages/performance/README.md +++ b/packages/performance/README.md @@ -359,9 +359,9 @@ Records a trace given its name and options. #### GetAttributesResult -| Prop | Type | Description | Since | -| ------------ | ------------------------------------- | ------------------------------------------------------------ | ----- | -| **`result`** | { [k: string]: string; } | A map of all custom attributes of a trace with their values. | 6.3.0 | +| Prop | Type | Description | Since | +| ------------ | --------------------------------------- | ------------------------------------------------------------ | ----- | +| **`result`** | { [key: string]: string; } | A map of all custom attributes of a trace with their values. | 6.3.0 | #### GetAttributesOptions diff --git a/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/FirebasePerformance.java b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/FirebasePerformance.java index a32e5f41..b50aaa2b 100644 --- a/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/FirebasePerformance.java +++ b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/FirebasePerformance.java @@ -1,11 +1,20 @@ package io.capawesome.capacitorjs.plugins.firebase.performance; +import androidx.annotation.Nullable; import com.google.firebase.perf.metrics.Trace; +import io.capawesome.capacitorjs.plugins.firebase.performance.classes.results.GetAttributeResult; +import io.capawesome.capacitorjs.plugins.firebase.performance.classes.results.GetAttributesResult; +import io.capawesome.capacitorjs.plugins.firebase.performance.classes.results.GetMetricResult; import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; public class FirebasePerformance { private HashMap traces = new HashMap(); + private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); public void startTrace(String traceName) { Trace trace = this.getFirebasePerformanceInstance().newTrace(traceName); @@ -39,6 +48,61 @@ public Boolean isEnabled() { return this.getFirebasePerformanceInstance().isPerformanceCollectionEnabled(); } + public static void putAttribute(Trace trace, String attribute, String value) { + trace.putAttribute(attribute, value); + } + + public static GetAttributeResult getAttribute(Trace trace, String attribute) { + return new GetAttributeResult(trace.getAttribute(attribute)); + } + + public static GetAttributesResult getAttributes(Trace trace) { + return new GetAttributesResult(trace.getAttributes()); + } + + public static void removeAttribute(Trace trace, String attribute) { + trace.removeAttribute(attribute); + return; + } + + public static void putMetric(Trace trace, String metricName, long num) { + trace.putMetric(metricName, num); + } + + public static GetMetricResult getMetric(Trace trace, String metricName) { + return new GetMetricResult(trace.getLongMetric(metricName)); + } + + public void record( + Trace trace, + String traceName, + long startTime, + long duration, + @Nullable Map attributes, + @Nullable Map metrics + ) { + long currentTime = System.currentTimeMillis(); + long startDelay = Math.max(0, (startTime - currentTime)); + if (attributes != null) { + for (String key : attributes.keySet()) { + FirebasePerformance.putAttribute(trace, key, attributes.get(key)); + } + } + if (metrics != null) { + for (String key : metrics.keySet()) { + FirebasePerformance.putMetric(trace, key, metrics.get(key)); + } + } + this.scheduler.schedule( + () -> { + this.startTrace(traceName); + scheduler.schedule(() -> this.stopTrace(traceName), duration, TimeUnit.MILLISECONDS); + }, + startDelay, + TimeUnit.MILLISECONDS + ); + } + private com.google.firebase.perf.FirebasePerformance getFirebasePerformanceInstance() { return com.google.firebase.perf.FirebasePerformance.getInstance(); } diff --git a/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/FirebasePerformancePlugin.java b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/FirebasePerformancePlugin.java index e51d144e..d40d402d 100644 --- a/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/FirebasePerformancePlugin.java +++ b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/FirebasePerformancePlugin.java @@ -7,6 +7,14 @@ import com.getcapacitor.PluginMethod; import com.getcapacitor.annotation.CapacitorPlugin; import com.google.firebase.perf.metrics.Trace; +import io.capawesome.capacitorjs.plugins.firebase.performance.classes.options.GetAttributeOptions; +import io.capawesome.capacitorjs.plugins.firebase.performance.classes.options.GetMetricOptions; +import io.capawesome.capacitorjs.plugins.firebase.performance.classes.options.PutAttributeOptions; +import io.capawesome.capacitorjs.plugins.firebase.performance.classes.options.PutMetricOptions; +import io.capawesome.capacitorjs.plugins.firebase.performance.classes.options.RecordOptions; +import io.capawesome.capacitorjs.plugins.firebase.performance.classes.results.GetAttributeResult; +import io.capawesome.capacitorjs.plugins.firebase.performance.classes.results.GetAttributesResult; +import io.capawesome.capacitorjs.plugins.firebase.performance.classes.results.GetMetricResult; @CapacitorPlugin(name = "FirebasePerformance") public class FirebasePerformancePlugin extends Plugin { @@ -17,6 +25,12 @@ public class FirebasePerformancePlugin extends Plugin { public static final String ERROR_TRACE_NAME_ALREADY_ASSIGNED = "traceName already assigned."; public static final String ERROR_ENABLED_MISSING = "enabled must be provided."; public static final String ERROR_TRACE_NOT_FOUND = "No trace was found with the provided traceName."; + public static final String ERROR_ATTRIBUTE_MISSING = "attribute must be provided."; + public static final String ERROR_VALUE_MISSING = "value must be provided."; + public static final String ERROR_NUM_MISSING = "num must be provided."; + public static final String ERROR_START_TIME_MISSING = "startTime must be provided."; + public static final String ERROR_DURATION_MISSING = "duration must be provided."; + public static final String ERROR_INVALID_METRIC_VALUE = "provided metric value is not a number."; private FirebasePerformance implementation = new FirebasePerformance(); @PluginMethod @@ -116,4 +130,134 @@ public void isEnabled(PluginCall call) { call.reject(exception.getMessage()); } } + + @PluginMethod(returnType = PluginMethod.RETURN_NONE) + public void putAttribute(PluginCall call) { + try { + PutAttributeOptions options = new PutAttributeOptions(call); + Trace trace = implementation.getTraceByName(options.getTraceName()); + if (trace == null) { + call.reject(ERROR_TRACE_NOT_FOUND); + return; + } + FirebasePerformance.putAttribute(trace, options.getAttribute(), options.getValue()); + call.resolve(); + } catch (Exception exception) { + Logger.error(TAG, exception.getMessage(), exception); + call.reject(exception.getMessage()); + } + } + + @PluginMethod(returnType = PluginMethod.RETURN_PROMISE) + public void getAttribute(PluginCall call) { + try { + GetAttributeOptions options = new GetAttributeOptions(call); + Trace trace = implementation.getTraceByName(options.getTraceName()); + if (trace == null) { + call.reject(ERROR_TRACE_NOT_FOUND); + return; + } + GetAttributeResult result = FirebasePerformance.getAttribute(trace, options.getAttribute()); + call.resolve(result.toJSObject()); + } catch (Exception exception) { + Logger.error(TAG, exception.getMessage(), exception); + call.reject(exception.getMessage()); + } + } + + @PluginMethod(returnType = PluginMethod.RETURN_PROMISE) + public void getAttributes(PluginCall call) { + try { + String traceName = call.getString("traceName"); + if (traceName == null) { + call.reject(ERROR_TRACE_NAME_MISSING); + return; + } + Trace trace = implementation.getTraceByName(traceName); + if (trace == null) { + call.reject(ERROR_TRACE_NOT_FOUND); + return; + } + GetAttributesResult result = FirebasePerformance.getAttributes(trace); + call.resolve(result.toJSObject()); + } catch (Exception exception) { + Logger.error(TAG, exception.getMessage(), exception); + call.reject(exception.getMessage()); + } + } + + @PluginMethod(returnType = PluginMethod.RETURN_NONE) + public void removeAttribute(PluginCall call) { + try { + GetAttributeOptions options = new GetAttributeOptions(call); + Trace trace = implementation.getTraceByName(options.getTraceName()); + if (trace == null) { + call.reject(ERROR_TRACE_NOT_FOUND); + return; + } + FirebasePerformance.removeAttribute(trace, options.getAttribute()); + call.resolve(); + } catch (Exception exception) { + Logger.error(TAG, exception.getMessage(), exception); + call.reject(exception.getMessage()); + } + } + + @PluginMethod(returnType = PluginMethod.RETURN_NONE) + public void putMetric(PluginCall call) { + try { + PutMetricOptions options = new PutMetricOptions(call); + Trace trace = implementation.getTraceByName(options.getTraceName()); + if (trace == null) { + call.reject(ERROR_TRACE_NOT_FOUND); + return; + } + FirebasePerformance.putMetric(trace, options.getMetricName(), options.getNum()); + call.resolve(); + } catch (Exception exception) { + Logger.error(TAG, exception.getMessage(), exception); + call.reject(exception.getMessage()); + } + } + + @PluginMethod(returnType = PluginMethod.RETURN_PROMISE) + public void getMetric(PluginCall call) { + try { + GetMetricOptions options = new GetMetricOptions(call); + Trace trace = implementation.getTraceByName(options.getTraceName()); + if (trace == null) { + call.reject(ERROR_TRACE_NOT_FOUND); + return; + } + GetMetricResult result = FirebasePerformance.getMetric(trace, options.getMetricName()); + call.resolve(result.toJSObject()); + } catch (Exception exception) { + Logger.error(TAG, exception.getMessage(), exception); + call.reject(exception.getMessage()); + } + } + + @PluginMethod(returnType = PluginMethod.RETURN_NONE) + public void record(PluginCall call) { + try { + RecordOptions options = new RecordOptions(call); + Trace trace = implementation.getTraceByName(options.getTraceName()); + if (trace == null) { + call.reject(ERROR_TRACE_NOT_FOUND); + return; + } + implementation.record( + trace, + options.getTraceName(), + options.getStartTime(), + options.getDuration(), + options.getAttributes(), + options.getMetrics() + ); + call.resolve(); + } catch (Exception exception) { + Logger.error(TAG, exception.getMessage(), exception); + call.reject(exception.getMessage()); + } + } } diff --git a/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/options/GetAttributeOptions.java b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/options/GetAttributeOptions.java new file mode 100644 index 00000000..faf64037 --- /dev/null +++ b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/options/GetAttributeOptions.java @@ -0,0 +1,38 @@ +package io.capawesome.capacitorjs.plugins.firebase.performance.classes.options; + +import com.getcapacitor.PluginCall; +import io.capawesome.capacitorjs.plugins.firebase.performance.FirebasePerformancePlugin; + +public class GetAttributeOptions { + + private final String traceName; + private final String attribute; + + public static class GetAttributeOptionsException extends Exception { + + public GetAttributeOptionsException(String message) { + super(message); + } + } + + public GetAttributeOptions(PluginCall call) throws GetAttributeOptionsException { + String traceName = call.getString("traceName"); + if (traceName == null) { + throw new GetAttributeOptionsException(FirebasePerformancePlugin.ERROR_TRACE_NAME_MISSING); + } + this.traceName = traceName; + String attribute = call.getString("attribute"); + if (attribute == null) { + throw new GetAttributeOptionsException(FirebasePerformancePlugin.ERROR_ATTRIBUTE_MISSING); + } + this.attribute = attribute; + } + + public String getTraceName() { + return traceName; + } + + public String getAttribute() { + return attribute; + } +} diff --git a/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/options/GetMetricOptions.java b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/options/GetMetricOptions.java new file mode 100644 index 00000000..48e7f747 --- /dev/null +++ b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/options/GetMetricOptions.java @@ -0,0 +1,38 @@ +package io.capawesome.capacitorjs.plugins.firebase.performance.classes.options; + +import com.getcapacitor.PluginCall; +import io.capawesome.capacitorjs.plugins.firebase.performance.FirebasePerformancePlugin; + +public class GetMetricOptions { + + private final String traceName; + private final String metricName; + + public static class GetMetricOptionsException extends Exception { + + public GetMetricOptionsException(String message) { + super(message); + } + } + + public GetMetricOptions(PluginCall call) throws GetMetricOptionsException { + String traceName = call.getString("traceName"); + if (traceName == null) { + throw new GetMetricOptionsException(FirebasePerformancePlugin.ERROR_TRACE_NAME_MISSING); + } + this.traceName = traceName; + String metricName = call.getString("metricName"); + if (metricName == null) { + throw new GetMetricOptionsException(FirebasePerformancePlugin.ERROR_METRIC_NAME_MISSING); + } + this.metricName = metricName; + } + + public String getTraceName() { + return traceName; + } + + public String getMetricName() { + return metricName; + } +} diff --git a/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/options/PutAttributeOptions.java b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/options/PutAttributeOptions.java new file mode 100644 index 00000000..dd9591af --- /dev/null +++ b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/options/PutAttributeOptions.java @@ -0,0 +1,48 @@ +package io.capawesome.capacitorjs.plugins.firebase.performance.classes.options; + +import com.getcapacitor.PluginCall; +import io.capawesome.capacitorjs.plugins.firebase.performance.FirebasePerformancePlugin; + +public class PutAttributeOptions { + + private final String traceName; + private final String attribute; + private final String value; + + public static class PutAttributeOptionsException extends Exception { + + public PutAttributeOptionsException(String message) { + super(message); + } + } + + public PutAttributeOptions(PluginCall call) throws PutAttributeOptionsException { + String traceName = call.getString("traceName"); + if (traceName == null) { + throw new PutAttributeOptionsException(FirebasePerformancePlugin.ERROR_TRACE_NAME_MISSING); + } + this.traceName = traceName; + String attribute = call.getString("attribute"); + if (attribute == null) { + throw new PutAttributeOptionsException(FirebasePerformancePlugin.ERROR_ATTRIBUTE_MISSING); + } + this.attribute = attribute; + String value = call.getString("value"); + if (value == null) { + throw new PutAttributeOptionsException(FirebasePerformancePlugin.ERROR_VALUE_MISSING); + } + this.value = value; + } + + public String getTraceName() { + return traceName; + } + + public String getAttribute() { + return attribute; + } + + public String getValue() { + return value; + } +} diff --git a/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/options/PutMetricOptions.java b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/options/PutMetricOptions.java new file mode 100644 index 00000000..2917d6fc --- /dev/null +++ b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/options/PutMetricOptions.java @@ -0,0 +1,48 @@ +package io.capawesome.capacitorjs.plugins.firebase.performance.classes.options; + +import com.getcapacitor.PluginCall; +import io.capawesome.capacitorjs.plugins.firebase.performance.FirebasePerformancePlugin; + +public class PutMetricOptions { + + private final String traceName; + private final String metricName; + private final long num; + + public static class PutMetricOptionsException extends Exception { + + public PutMetricOptionsException(String message) { + super(message); + } + } + + public PutMetricOptions(PluginCall call) throws PutMetricOptionsException { + String traceName = call.getString("traceName"); + if (traceName == null) { + throw new PutMetricOptionsException(FirebasePerformancePlugin.ERROR_TRACE_NAME_MISSING); + } + this.traceName = traceName; + String metricName = call.getString("metricName"); + if (metricName == null) { + throw new PutMetricOptionsException(FirebasePerformancePlugin.ERROR_METRIC_NAME_MISSING); + } + this.metricName = metricName; + Double num = call.getDouble("num"); + if (num == null) { + throw new PutMetricOptionsException(FirebasePerformancePlugin.ERROR_NUM_MISSING); + } + this.num = (long) Math.floor(num); + } + + public String getTraceName() { + return traceName; + } + + public String getMetricName() { + return metricName; + } + + public long getNum() { + return num; + } +} diff --git a/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/options/RecordOptions.java b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/options/RecordOptions.java new file mode 100644 index 00000000..2bd6beec --- /dev/null +++ b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/options/RecordOptions.java @@ -0,0 +1,105 @@ +package io.capawesome.capacitorjs.plugins.firebase.performance.classes.options; + +import androidx.annotation.Nullable; +import com.getcapacitor.JSObject; +import com.getcapacitor.PluginCall; +import io.capawesome.capacitorjs.plugins.firebase.performance.FirebasePerformancePlugin; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import org.json.JSONException; +import org.json.JSONObject; + +public class RecordOptions { + + private final String traceName; + private final long startTime; + private final long duration; + private final Map metrics; + private final Map attributes; + + public static class RecordOptionsException extends Exception { + + public RecordOptionsException(String message) { + super(message); + } + } + + public RecordOptions(PluginCall call) throws RecordOptionsException, JSONException { + String traceName = call.getString("traceName"); + if (traceName == null) { + throw new RecordOptionsException(FirebasePerformancePlugin.ERROR_TRACE_NAME_MISSING); + } + this.traceName = traceName; + Long startTime = call.getLong("startTime"); + if (startTime == null) { + throw new RecordOptionsException(FirebasePerformancePlugin.ERROR_START_TIME_MISSING); + } + this.startTime = startTime; + Long duration = call.getLong("duration"); + if (duration == null) { + throw new RecordOptionsException(FirebasePerformancePlugin.ERROR_DURATION_MISSING); + } + this.duration = duration; + JSObject metrics = call.getObject("metrics", new JSObject()); + if (metrics == null) { + this.metrics = null; + } else { + this.metrics = jsObjectToMetricsMap(metrics); + } + JSObject attributes = call.getObject("attributes", new JSObject()); + if (attributes == null) { + this.attributes = null; + } else { + this.attributes = jsObjectToAttributesMap(attributes); + } + } + + public String getTraceName() { + return traceName; + } + + public long getStartTime() { + return startTime; + } + + public long getDuration() { + return duration; + } + + @Nullable + public Map getAttributes() { + return attributes; + } + + @Nullable + public Map getMetrics() { + return metrics; + } + + private static Map jsObjectToAttributesMap(JSONObject object) throws JSONException { + Map map = new HashMap<>(); + Iterator keys = object.keys(); + while (keys.hasNext()) { + String key = keys.next(); + map.put(key, object.get(key).toString()); + } + return map; + } + + private static Map jsObjectToMetricsMap(JSONObject object) throws JSONException { + Map map = new HashMap<>(); + Iterator keys = object.keys(); + while (keys.hasNext()) { + String key = keys.next(); + if (object.get(key) instanceof Long) { + map.put(key, (Long) object.get(key)); + } else if (object.get(key) instanceof Double) { + map.put(key, (long) Math.floor((double) object.get(key))); + } else { + throw new JSONException(FirebasePerformancePlugin.ERROR_INVALID_METRIC_VALUE); + } + } + return map; + } +} diff --git a/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/results/GetAttributeResult.java b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/results/GetAttributeResult.java new file mode 100644 index 00000000..5c4ab558 --- /dev/null +++ b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/results/GetAttributeResult.java @@ -0,0 +1,19 @@ +package io.capawesome.capacitorjs.plugins.firebase.performance.classes.results; + +import androidx.annotation.Nullable; +import com.getcapacitor.JSObject; + +public class GetAttributeResult { + + private final String value; + + public GetAttributeResult(@Nullable String value) { + this.value = value; + } + + public JSObject toJSObject() { + JSObject result = new JSObject(); + result.put("value", this.value); + return result; + } +} diff --git a/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/results/GetAttributesResult.java b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/results/GetAttributesResult.java new file mode 100644 index 00000000..5f6cac68 --- /dev/null +++ b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/results/GetAttributesResult.java @@ -0,0 +1,24 @@ +package io.capawesome.capacitorjs.plugins.firebase.performance.classes.results; + +import androidx.annotation.NonNull; +import com.getcapacitor.JSObject; +import java.util.Map; + +public class GetAttributesResult { + + private final Map attributesMap; + + public GetAttributesResult(@NonNull Map value) { + this.attributesMap = value; + } + + public JSObject toJSObject() { + JSObject result = new JSObject(); + JSObject resultMap = new JSObject(); + for (String attribute : this.attributesMap.keySet()) { + resultMap.put(attribute, this.attributesMap.get(attribute)); + } + result.put("result", resultMap); + return result; + } +} diff --git a/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/results/GetMetricResult.java b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/results/GetMetricResult.java new file mode 100644 index 00000000..5434c566 --- /dev/null +++ b/packages/performance/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/performance/classes/results/GetMetricResult.java @@ -0,0 +1,19 @@ +package io.capawesome.capacitorjs.plugins.firebase.performance.classes.results; + +import androidx.annotation.NonNull; +import com.getcapacitor.JSObject; + +public class GetMetricResult { + + private final long num; + + public GetMetricResult(@NonNull long value) { + this.num = value; + } + + public JSObject toJSObject() { + JSObject result = new JSObject(); + result.put("value", this.num == 0 ? null : this.num); + return result; + } +} diff --git a/packages/performance/src/definitions.ts b/packages/performance/src/definitions.ts index 33744152..27e27b1c 100644 --- a/packages/performance/src/definitions.ts +++ b/packages/performance/src/definitions.ts @@ -162,12 +162,14 @@ export interface PutAttributeOptions { * Name of the attribute to set its value. * * @since 6.3.0 + * @example "experiment" */ attribute: string; /** * The value to set to the attribute. * * @since 6.3.0 + * @example "A" */ value: string; }