Skip to content

Commit 44e597a

Browse files
feat: support app flows
1 parent c2079ad commit 44e597a

File tree

9 files changed

+254
-14
lines changed

9 files changed

+254
-14
lines changed

android/src/main/java/com/instabug/reactlibrary/RNInstabugAPMModule.java

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import android.os.SystemClock;
55
import android.util.Log;
66

7+
import androidx.annotation.NonNull;
78
import com.facebook.react.bridge.Promise;
89
import com.facebook.react.bridge.ReactApplicationContext;
910
import com.facebook.react.bridge.ReactContextBaseJavaModule;
@@ -123,6 +124,84 @@ public void run() {
123124
});
124125
}
125126

127+
/**
128+
* Starts an AppFlow with the specified name.
129+
* <br/>
130+
* On starting two flows with the same name the older flow will end with force abandon end reason.
131+
* AppFlow name cannot exceed 150 characters otherwise it's truncated,
132+
* leading and trailing whitespaces are also ignored.
133+
*
134+
* @param name AppFlow name. It can not be empty string or null.
135+
* Starts a new AppFlow, if APM is enabled, feature is enabled
136+
* and Instabug SDK is initialised.
137+
*/
138+
@ReactMethod
139+
public void startFlow(@NonNull final String name) {
140+
MainThreadHandler.runOnMainThread(new Runnable() {
141+
@Override
142+
public void run() {
143+
try {
144+
APM.startFlow(name);
145+
} catch (Exception e) {
146+
e.printStackTrace();
147+
}
148+
}
149+
});
150+
}
151+
152+
/**
153+
* Sets custom attributes for AppFlow with a given name.
154+
* <br/>
155+
* Setting an attribute value to null will remove its corresponding key if it already exists.
156+
* <br/>
157+
* Attribute key name cannot exceed 30 characters.
158+
* Leading and trailing whitespaces are also ignored.
159+
* Does not accept empty strings or null.
160+
* <br/>
161+
* Attribute value name cannot exceed 60 characters,
162+
* leading and trailing whitespaces are also ignored.
163+
* Does not accept empty strings.
164+
* <br/>
165+
* If a trace is ended, attributes will not be added and existing ones will not be updated.
166+
* <br/>
167+
*
168+
* @param name AppFlow name. It can not be empty string or null
169+
* @param key AppFlow attribute value. It can not be empty string or null
170+
* @param value AppFlow attribute key. It can not be empty string. Null to remove attribute
171+
*/
172+
@ReactMethod
173+
public void setFlowAttribute(@NonNull final String name, @NonNull final String key, final String value) {
174+
MainThreadHandler.runOnMainThread(new Runnable() {
175+
@Override
176+
public void run() {
177+
try {
178+
APM.setFlowAttribute(name, key, value);
179+
} catch (Exception e) {
180+
e.printStackTrace();
181+
}
182+
}
183+
});
184+
}
185+
186+
/**
187+
* Ends AppFlow with a given name.
188+
*
189+
* @param name AppFlow name to be ended. It can not be empty string or null
190+
*/
191+
@ReactMethod
192+
public void endFlow(@NonNull final String name) {
193+
MainThreadHandler.runOnMainThread(new Runnable() {
194+
@Override
195+
public void run() {
196+
try {
197+
APM.endFlow(name);
198+
} catch (Exception e) {
199+
e.printStackTrace();
200+
}
201+
}
202+
});
203+
}
204+
126205
/**
127206
* Starts an execution trace
128207
*
@@ -234,7 +313,7 @@ public void run() {
234313
}
235314

236315
/**
237-
* Send Apm network log by Reflection
316+
* Send Apm network log by Reflection
238317
*/
239318
@ReactMethod
240319
public void networkLog(String networkData) throws JSONException {
@@ -256,8 +335,8 @@ public void networkLog(String networkData) throws JSONException {
256335
final Integer statusCode = (Integer) jsonObject.get("responseCode");
257336
final long requestDuration = ((Number) jsonObject.get("duration")).longValue();
258337
final long requestStartTime = ((Number) jsonObject.get("startTime")).longValue() * 1000;
259-
final String requestHeaders = (String) jsonObject.get("requestHeaders").toString();
260-
final String responseHeaders = (String) jsonObject.get("responseHeaders").toString();
338+
final String requestHeaders = (String) jsonObject.get("requestHeaders").toString();
339+
final String responseHeaders = (String) jsonObject.get("responseHeaders").toString();
261340
final String errorMessage;
262341
if(errorDomain.equals("")) {
263342
errorMessage = null;
@@ -268,9 +347,9 @@ public void networkLog(String networkData) throws JSONException {
268347
String gqlQueryName = null;
269348
if(jsonObject.has("gqlQueryName")){
270349
gqlQueryName = (String) jsonObject.get("gqlQueryName");
271-
}
350+
}
272351
final String serverErrorMessage = (String) jsonObject.get("serverErrorMessage");
273-
352+
274353
try {
275354
Method method = getMethod(Class.forName("com.instabug.apm.networking.APMNetworkLogger"), "log", long.class, long.class, String.class, String.class, long.class, String.class, String.class, String.class, String.class, String.class, long.class, int.class, String.class, String.class, String.class, String.class);
276355
if (method != null) {

android/src/test/java/com/instabug/reactlibrary/RNInstabugAPMModuleTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,37 @@ public void givenTruesetEnabled_whenQuery_thenShouldCallNativeApiWithEnabled() {
125125
verify(promise).resolve(any());
126126
}
127127

128+
@Test
129+
public void testStartFlow() {
130+
String appFlowName = "appFlowName";
131+
132+
apmModule.startFlow(appFlowName);
133+
134+
mockAPM.verify(() -> APM.startFlow(appFlowName));
135+
mockAPM.verifyNoMoreInteractions();
136+
}
137+
138+
@Test
139+
public void testEndFlow() {
140+
String appFlowName = "appFlowName";
141+
142+
apmModule.endFlow(appFlowName);
143+
144+
mockAPM.verify(() -> APM.endFlow(appFlowName));
145+
mockAPM.verifyNoMoreInteractions();
146+
}
147+
148+
@Test
149+
public void testSetFlowAttribute() {
150+
String appFlowName = "appFlowName";
151+
String flowAttributeKey = "attributeKey";
152+
String flowAttributeValue = "attributeValue";
153+
apmModule.setFlowAttribute(appFlowName, flowAttributeKey, flowAttributeValue);
154+
155+
mockAPM.verify(() -> APM.setFlowAttribute(appFlowName, flowAttributeKey, flowAttributeValue));
156+
mockAPM.verifyNoMoreInteractions();
157+
}
158+
128159
// @Test
129160
// public void givenString$setExecutionTraceAttribute_whenQuery_thenShouldCallNativeApiWithIntArgs() {
130161
// // given

examples/default/ios/InstabugTests/InstabugAPMTests.m

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,15 @@ - (void) testSetAPMEnabled {
6262
- (void) testSetAppLaunchEnabled {
6363
id mock = OCMClassMock([IBGAPM class]);
6464
BOOL isEnabled = YES;
65-
65+
6666
OCMStub([mock setColdAppLaunchEnabled:isEnabled]);
6767
[self.instabugBridge setAppLaunchEnabled:isEnabled];
6868
OCMVerify([mock setColdAppLaunchEnabled:isEnabled]);
6969
}
7070

7171
- (void) testEndAppLaunch {
7272
id mock = OCMClassMock([IBGAPM class]);
73-
73+
7474
OCMStub([mock endAppLaunch]);
7575
[self.instabugBridge endAppLaunch];
7676
OCMVerify([mock endAppLaunch]);
@@ -79,7 +79,7 @@ - (void) testEndAppLaunch {
7979
- (void) testSetAutoUITraceEnabled {
8080
id mock = OCMClassMock([IBGAPM class]);
8181
BOOL isEnabled = YES;
82-
82+
8383
OCMStub([mock setAutoUITraceEnabled:isEnabled]);
8484
[self.instabugBridge setAutoUITraceEnabled:isEnabled];
8585
OCMVerify([mock setAutoUITraceEnabled:isEnabled]);
@@ -91,7 +91,7 @@ - (void) testStartExecutionTrace {
9191
NSString* traceKey = @"1";
9292
RCTPromiseResolveBlock resolve = ^(id result) {};
9393
RCTPromiseRejectBlock reject = ^(NSString *code, NSString *message, NSError *error) {};
94-
94+
9595
OCMStub([mock startExecutionTraceWithName:traceName]);
9696
[self.instabugBridge startExecutionTrace:traceName :traceKey :resolve :reject];
9797
OCMVerify([mock startExecutionTraceWithName:traceName]);
@@ -107,10 +107,10 @@ - (void) testSetExecutionTraceAttribute {
107107
IBGExecutionTrace * trace = [IBGExecutionTrace alloc];
108108
id mock = OCMClassMock([IBGAPM class]);
109109
id traceMock = OCMPartialMock(trace);
110-
110+
111111
OCMStub([mock startExecutionTraceWithName:traceName]).andReturn(trace);
112112
[self.instabugBridge startExecutionTrace:traceName :traceId :resolve :reject];
113-
113+
114114
OCMStub([traceMock setAttributeWithKey:traceKey value:traceValue]);
115115
[self.instabugBridge setExecutionTraceAttribute:traceId :traceKey :traceValue];
116116
OCMVerify([traceMock setAttributeWithKey:traceKey value:traceValue]);
@@ -127,24 +127,52 @@ - (void) testEndExecutionTrace {
127127

128128
OCMStub([apmMock startExecutionTraceWithName:traceName]).andReturn(trace);
129129
[self.instabugBridge startExecutionTrace:traceName :traceId :resolve :reject];
130-
130+
131131
OCMStub([traceMock end]);
132132
[self.instabugBridge endExecutionTrace:traceId];
133133
OCMVerify([traceMock end]);
134134
}
135135

136+
- (void) testStartFlow {
137+
id mock = OCMClassMock([IBGAPM class]);
138+
NSString* appFlowName = @"APP_Flow_1";
139+
140+
OCMStub([mock startFlowWithName:appFlowName]);
141+
[self.instabugBridge startFlow:appFlowName];
142+
OCMVerify([mock startFlowWithName:appFlowName]);
143+
}
144+
145+
- (void) testEndFlow {
146+
id mock = OCMClassMock([IBGAPM class]);
147+
NSString* appFlowName = @"APP_Flow_1";
148+
149+
OCMStub([mock endFlowWithName:appFlowName]);
150+
[self.instabugBridge endFlow:appFlowName];
151+
OCMVerify([mock endFlowWithName:appFlowName]);
152+
}
153+
154+
- (void) testSetFlowAttribute {
155+
id mock = OCMClassMock([IBGAPM class]);
156+
NSString* appFlowName = @"APP_Flow_1";
157+
NSString* attributeKey = @"Attribute_Key_1";
158+
NSString* attributeValue = @"Attribute_Value_1";
159+
160+
[self.instabugBridge setFlowAttribute:appFlowName :attributeKey :attributeValue];
161+
OCMVerify([mock setAttributeForFlowWithName:appFlowName key:attributeKey value:attributeValue]);
162+
}
163+
136164
- (void) testStartUITrace {
137165
id mock = OCMClassMock([IBGAPM class]);
138166
NSString* traceName = @"UITrace_1";
139-
167+
140168
OCMStub([mock startUITraceWithName:traceName]);
141169
[self.instabugBridge startUITrace:traceName];
142170
OCMVerify([mock startUITraceWithName:traceName]);
143171
}
144172

145173
- (void) testEndUITrace {
146174
id mock = OCMClassMock([IBGAPM class]);
147-
175+
148176
OCMStub([mock endUITrace]);
149177
[self.instabugBridge endUITrace];
150178
OCMVerify([mock endUITrace]);

ios/RNInstabug/InstabugAPMBridge.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
- (void)setExecutionTraceAttribute:(NSString *)id:(NSString *)key
2222
:(NSString *)value DEPRECATED_MSG_ATTRIBUTE("Please use APM.setTraceAttribute instead.");
2323
- (void)endExecutionTrace:(NSString *)id DEPRECATED_MSG_ATTRIBUTE("Please use APM.endFlow instead.");
24+
- (void)startFlow:(NSString *)name;
25+
- (void)endFlow:(NSString *)name;
26+
- (void)setFlowAttribute:(NSString *)name :(NSString *)key :(NSString *_Nullable)value;
2427
- (void)startUITrace:(NSString *)name;
2528
- (void)endUITrace;
2629

ios/RNInstabug/InstabugAPMBridge.m

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,19 @@ - (id) init
8888
}
8989
}
9090

91+
RCT_EXPORT_METHOD(startFlow: (NSString *)name) {
92+
[IBGAPM startFlowWithName:name];
93+
}
94+
95+
RCT_EXPORT_METHOD(endFlow: (NSString *)name) {
96+
[IBGAPM endFlowWithName:name];
97+
}
98+
99+
100+
RCT_EXPORT_METHOD(setFlowAttribute:(NSString *)name :(NSString *)key :(NSString *_Nullable)value) {
101+
[IBGAPM setAttributeForFlowWithName:name key:key value:value];
102+
}
103+
91104
RCT_EXPORT_METHOD(startUITrace:(NSString *)name) {
92105
[IBGAPM startUITraceWithName:name];
93106
}

src/modules/APM.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,51 @@ export const startExecutionTrace = async (name: string): Promise<Trace> => {
6868
return new Trace(id, name);
6969
};
7070

71+
/**
72+
* Starts an AppFlow with the specified name.
73+
*
74+
* On starting two flows with the same name, the older flow will end with a force abandon end reason.
75+
* The AppFlow name cannot exceed 150 characters; otherwise, it's truncated,
76+
* leading and trailing whitespaces are also ignored.
77+
*
78+
* @param name - The name of the AppFlow. It cannot be an empty string or null.
79+
* A new AppFlow is started if APM is enabled, the feature is enabled,
80+
* and the Instabug SDK is initialized.
81+
*/
82+
export const startFlow = (name: string) => {
83+
NativeAPM.startFlow(name);
84+
};
85+
86+
/**
87+
* Ends an AppFlow with the given name.
88+
*
89+
* @param name - The name of the AppFlow to end. It cannot be an empty string or null.
90+
*/
91+
export const endFlow = (name: string) => {
92+
NativeAPM.endFlow(name);
93+
};
94+
95+
/**
96+
* Sets custom attributes for an AppFlow with a given name.
97+
*
98+
* Setting an attribute value to null will remove the corresponding key if it already exists.
99+
* Attribute keys cannot exceed 30 characters and leading/trailing whitespaces are ignored.
100+
* Empty strings or null for attribute keys are not accepted.
101+
*
102+
* Attribute values cannot exceed 60 characters and leading/trailing whitespaces are ignored.
103+
* Empty strings for attribute values are not accepted, but null can be used to remove an attribute.
104+
*
105+
* If an AppFlow is ended, attributes will not be added and existing ones will not be updated.
106+
*
107+
* @param name - The name of the AppFlow. It cannot be an empty string or null.
108+
* @param key - The key of the attribute. It cannot be an empty string or null.
109+
* @param [value] - The value of the attribute. It cannot be an empty string. Use null to remove the attribute.
110+
*/
111+
112+
export const setFlowAttribute = (name: string, key: string, value?: string) => {
113+
NativeAPM.setFlowAttribute(name, key, value);
114+
};
115+
71116
/**
72117
* Starts a custom trace
73118
* @param name

src/native/NativeAPM.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ export interface ApmNativeModule extends NativeModule {
1818
setExecutionTraceAttribute(id: string, key: string, value: string): void;
1919
endExecutionTrace(id: string): void;
2020

21+
// App Flows APIs //
22+
startFlow(name: string): void;
23+
endFlow(name: string): void;
24+
setFlowAttribute(name: string, key: string, value?: string): void;
25+
2126
// UI Traces APIs //
2227
setAutoUITraceEnabled(isEnabled: boolean): void;
2328
startUITrace(name: string): void;

test/mocks/mockAPM.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ const mockAPM: ApmNativeModule = {
99
startExecutionTrace: jest.fn(),
1010
setExecutionTraceAttribute: jest.fn(),
1111
endExecutionTrace: jest.fn(),
12+
startFlow: jest.fn(),
13+
setFlowAttribute: jest.fn(),
14+
endFlow: jest.fn(),
1215
startUITrace: jest.fn(),
1316
endUITrace: jest.fn(),
1417
endAppLaunch: jest.fn(),

0 commit comments

Comments
 (0)