From d6e9f14dd17562e7fb75a57b292e951b975f7edf Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Mon, 27 May 2024 05:50:41 +0300 Subject: [PATCH 1/6] fix: Replace Thrown Errors with Logs sss chore(ios): bump sdk to v13.1.0 (#1227) * chore(ios): bump ios sdk v13.1.0 * chore(ios): bump ios sdk v13.1.0 * chore(ios): bump ios sdk v13.1.0 chore(android): bump sdk to v13.1.1 (#1228) * chore(android): bump android sdk v13.1.1 feat: enhance non-fatals support (#1194) * add non fatal api --------- Co-authored-by: Ahmed Mahmoud <68241710+a7medev@users.noreply.github.com> fix: read env variable in sourcemap (#1232) * fix sourcemap issue * fix sourcemap issue * fix sourcemap issue * Update typo in CHANGELOG.md --------- Co-authored-by: Andrew Amin <160974398+AndrewAminInstabug@users.noreply.github.com> Release:v13.1.1 (#1231) * release/v13.1.1 * Update CHANGELOG.md Update CHANGELOG.md chore(deps): bump @babel/traverse in /examples/default Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.22.8 to 7.24.6. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.24.6/packages/babel-traverse) --- updated-dependencies: - dependency-name: "@babel/traverse" dependency-type: indirect ... Signed-off-by: dependabot[bot] chore(deps): bump follow-redirects from 1.15.2 to 1.15.6 Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.6. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.6) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] fix: cont work after logging fix(android): change parameters used in inner class to final (#1239) chore(android): bump sdk to v13.2.0 (#1245) * chore(android): bump sdk to v13.2.0 * chore: update changelog chore(ios): bump sdk to v13.2.0 (#1246) release: v13.2.0 (#1247) chore: resolve issues in changelog (#1249) feat(example): add apm screen (#1141) fix(android): resolve an OOM in network logs (#1244) fix(android): APM network logging(#1253) * fix(android): add W3C External Trace Attributes placeholder * chore: add CHANGLOG * chore: add CHANGLOG * fix: remove ios sub module feat: export upload utils (#1252) chore(example): remove flipper (#1259) fix(android): pass network start time in microseconds (#1260) * fix: network timestamp in android side * fix: PR comments Co-authored-by: Ahmed Elrefaey <68241710+a7medev@users.noreply.github.com> --------- Co-authored-by: Ahmed Elrefaey <68241710+a7medev@users.noreply.github.com> feat: support feature flags with variants (#1230) Jira ID: MOB-14684 --------- Co-authored-by: Ahmed Elrefaey <68241710+a7medev@users.noreply.github.com> chore(android): bump android sdk to v13.3.0 (#1261) chore(ios): bump sdk to v13.3.0 (#1262) release: v13.3.0 (#1263) chore: remove duplicate app flows entries in changelog (#1264) chore: remove duplicate execution traces deprecation in changelog (#1265) feat: navigation tracking support with expo router (#1270) * feat: add screen tracker on screen change listener and tests * feat (example): add screen change listener chore: enhance expo router tracking support (#1272) ci: generalize enterprise releases (#1275) ci: run tests before enterprise releases (#1271) ci: publish snapshots to npm (#1274) fix: PR comments fix(ios): network log empty response body (#1273) fix: drop non-error objects when reporting errors (#1279) * Fix: omitted non-error objects when logging errors * ci: publish snapshots to npm (#1274) * Fix: omitted non-error objects when logging errors * fix: use warn instead of logs Co-authored-by: Ahmed Elrefaey <68241710+a7medev@users.noreply.github.com> * Update CHANGELOG.md Co-authored-by: Ahmed Elrefaey <68241710+a7medev@users.noreply.github.com> * fix: merge issues --------- Co-authored-by: Ahmed Elrefaey <68241710+a7medev@users.noreply.github.com> feat: capture client error in the network interceptor (#1257) * feat/support-capture-client-error-in-xhr-requests --------- Co-authored-by: Abdelhamid Nasser <38096011+abdelhamid-f-nasser@users.noreply.github.com> Co-authored-by: Ahmed Elrefaey <68241710+a7medev@users.noreply.github.com> Co-authored-by: kholood fix: APM test cases --- .circleci/config.yml | 343 ++++++++++------- .gitignore | 2 + CHANGELOG.md | 69 +++- android/build.gradle | 2 +- android/native.gradle | 2 +- android/sourcemaps.gradle | 2 +- android/src/main/AndroidManifest.xml | 2 +- .../instabug/reactlibrary/ArgsRegistry.java | 9 + .../reactlibrary/RNInstabugAPMModule.java | 88 ++--- .../RNInstabugCrashReportingModule.java | 89 +++-- .../RNInstabugReactnativeModule.java | 123 +++++-- .../RNInstabugSessionReplayModule.java | 2 +- .../reactlibrary/utils/ReportUtil.java | 14 - .../RNInstabugCrashReportingModuleTest.java | 27 ++ .../RNInstabugReactnativeModuleTest.java | 54 ++- .../reactlibrary/util/GlobalMocks.java | 10 + .../reactlibrary/util/MockReflected.java | 11 + cli/UploadSoFiles.ts | 88 ----- cli/UploadSourcemaps.ts | 89 ----- cli/commands/UploadSoFiles.ts | 38 ++ cli/commands/UploadSourcemaps.ts | 40 ++ cli/index.ts | 4 +- cli/upload/index.ts | 2 + cli/upload/uploadSoFiles.ts | 112 ++++++ cli/upload/uploadSourcemaps.ts | 73 ++++ examples/default/android/app/build.gradle | 6 - .../react/example/ReactNativeFlipper.java | 75 ---- .../react/example/MainApplication.java | 1 - .../RNInstabugExampleReactnativePackage.java | 8 - .../app/src/main/res/raw/instabug_config.json | 4 + .../instabugexample/ReactNativeFlipper.java | 20 - examples/default/android/build.gradle | 8 + examples/default/android/gradle.properties | 3 - .../InstabugExample.xcodeproj/project.pbxproj | 16 +- .../ios/InstabugExample/Instabug.plist | Bin 0 -> 157 bytes .../InstabugCrashReportingTests.m | 24 ++ .../ios/InstabugTests/InstabugSampleTests.m | 102 +++++- .../ios/InstabugTests/RNInstabugTests.m | 1 - .../InstabugTests/Util/IBGCrashReporting+CP.h | 13 + examples/default/ios/Podfile | 16 - examples/default/ios/Podfile.lock | 130 +------ examples/default/package.json | 9 +- examples/default/src/App.tsx | 21 +- .../src/components/ClipboardTextInput.tsx | 36 ++ .../default/src/components/CustomButton.tsx | 31 ++ .../default/src/components/CustomImage.tsx | 17 + .../default/src/components/CustomText.tsx | 17 + .../default/src/components/InputField.tsx | 16 +- .../components/PasteFromClipboardButton.tsx | 24 ++ examples/default/src/navigation/HomeStack.tsx | 15 +- .../src/screens/CrashReportingScreen.tsx | 124 ++++++- examples/default/src/screens/HomeScreen.tsx | 1 + .../default/src/screens/SettingsScreen.tsx | 76 ++++ .../default/src/screens/apm/APMScreen.tsx | 17 + .../default/src/screens/apm/FlowsScreen.tsx | 63 ++++ .../default/src/screens/apm/NetworkScreen.tsx | 142 +++++++ .../default/src/screens/apm/TracesScreen.tsx | 70 ++++ examples/default/yarn.lock | 345 ++++++++++++++++-- ios/RNInstabug/ArgsRegistry.h | 3 + ios/RNInstabug/ArgsRegistry.m | 12 + ios/RNInstabug/InstabugBugReportingBridge.m | 1 - ios/RNInstabug/InstabugCrashReportingBridge.h | 11 +- ios/RNInstabug/InstabugCrashReportingBridge.m | 18 +- ios/RNInstabug/InstabugReactBridge.h | 22 +- ios/RNInstabug/InstabugReactBridge.m | 126 +++---- ios/RNInstabug/RCTConvert+InstabugEnums.m | 7 + ios/RNInstabug/Util/IBGCrashReporting+CP.h | 13 + ios/RNInstabug/Util/IBGNetworkLogger+CP.h | 16 + ios/native.rb | 3 +- ios/sourcemaps.sh | 4 +- package.json | 2 +- rollup.config.js | 39 +- scripts/customize-ios-endpoints.sh | 28 ++ scripts/dream-11-delete-unused-features.sh | 70 ++-- scripts/replace.js | 24 +- scripts/snapshot-branch.sh | 30 -- scripts/snapshot-comment.md | 4 +- scripts/snapshot-version.sh | 11 + src/models/FeatureFlag.ts | 12 + src/models/NonFatalOptions.ts | 16 + src/modules/CrashReporting.ts | 25 +- src/modules/Instabug.ts | 72 +++- src/modules/NetworkLogger.ts | 34 +- src/native/NativeAPM.ts | 19 +- src/native/NativeConstants.ts | 9 +- src/native/NativeCrashReporting.ts | 9 +- src/native/NativeInstabug.ts | 38 +- src/utils/Enums.ts | 10 + src/utils/InstabugConstants.ts | 13 +- src/utils/InstabugUtils.ts | 97 ++++- src/utils/UnhandledRejectionTracking.ts | 5 +- src/utils/XhrNetworkInterceptor.ts | 28 +- test/mocks/mockAPM.ts | 2 +- test/mocks/mockInstabug.ts | 6 +- test/mocks/mockInstabugUtils.ts | 2 + test/modules/APM.spec.ts | 1 - test/modules/CrashReporting.spec.ts | 23 +- test/modules/Instabug.spec.ts | 98 ++++- test/modules/NetworkLogger.spec.ts | 228 ++++++++---- test/utils/InstabugUtils.spec.ts | 121 +++++- test/utils/XhrNetworkInterceptor.spec.ts | 4 +- tsconfig.upload.json | 10 + upload/package.json | 5 + yarn.lock | 6 +- 104 files changed, 3075 insertions(+), 1008 deletions(-) delete mode 100755 cli/UploadSoFiles.ts delete mode 100755 cli/UploadSourcemaps.ts create mode 100755 cli/commands/UploadSoFiles.ts create mode 100755 cli/commands/UploadSourcemaps.ts create mode 100644 cli/upload/index.ts create mode 100755 cli/upload/uploadSoFiles.ts create mode 100644 cli/upload/uploadSourcemaps.ts delete mode 100644 examples/default/android/app/src/debug/java/com/instabug/react/example/ReactNativeFlipper.java create mode 100644 examples/default/android/app/src/main/res/raw/instabug_config.json delete mode 100644 examples/default/android/app/src/release/java/com/instabugexample/ReactNativeFlipper.java create mode 100644 examples/default/ios/InstabugExample/Instabug.plist create mode 100644 examples/default/ios/InstabugTests/Util/IBGCrashReporting+CP.h create mode 100644 examples/default/src/components/ClipboardTextInput.tsx create mode 100644 examples/default/src/components/CustomButton.tsx create mode 100644 examples/default/src/components/CustomImage.tsx create mode 100644 examples/default/src/components/CustomText.tsx create mode 100644 examples/default/src/components/PasteFromClipboardButton.tsx create mode 100644 examples/default/src/screens/apm/APMScreen.tsx create mode 100644 examples/default/src/screens/apm/FlowsScreen.tsx create mode 100644 examples/default/src/screens/apm/NetworkScreen.tsx create mode 100644 examples/default/src/screens/apm/TracesScreen.tsx create mode 100644 ios/RNInstabug/Util/IBGCrashReporting+CP.h create mode 100755 scripts/customize-ios-endpoints.sh mode change 100644 => 100755 scripts/dream-11-delete-unused-features.sh delete mode 100755 scripts/snapshot-branch.sh create mode 100755 scripts/snapshot-version.sh create mode 100644 src/models/FeatureFlag.ts create mode 100644 src/models/NonFatalOptions.ts create mode 100644 tsconfig.upload.json create mode 100644 upload/package.json diff --git a/.circleci/config.yml b/.circleci/config.yml index c218cf7e9..efca5acab 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -17,6 +17,12 @@ references: - e2e_ios - e2e_android + dream11_prepare_config: &dream11_prepare_config + prepare_steps: + - prepare_dream11 + requires: + - hold_test_dream11 + commands: install_node_modules: parameters: @@ -36,15 +42,28 @@ commands: parameters: working_directory: type: string + deployment: + type: boolean + default: false steps: - restore_cache: name: Restore Pods Cache keys: - v1-pods-{{ checksum "<< parameters.working_directory >>/Podfile.lock" }} - - run: - name: Install Pods - working_directory: << parameters.working_directory >> - command: pod install --deployment + - when: + condition: << parameters.deployment >> + steps: + - run: + name: Install Pods + working_directory: << parameters.working_directory >> + command: pod install --deployment + - unless: + condition: << parameters.deployment >> + steps: + - run: + name: Install Pods + working_directory: << parameters.working_directory >> + command: pod install - save_cache: name: Save Pods Cache key: v1-pods-{{ checksum "<< parameters.working_directory >>/Podfile.lock" }} @@ -59,10 +78,72 @@ commands: type: string replace: type: string + working_directory: + type: string + default: ~/project steps: - run: name: Find and Replace in << parameters.files >> command: node ~/project/scripts/replace.js << parameters.search >> << parameters.replace >> << parameters.files >> + working_directory: << parameters.working_directory >> + + prepare_custom_package: + parameters: + npm_package: + type: string + android_package: + type: string + api_endpoint: + type: string + working_directory: + type: string + default: ~/project + steps: + - run: + name: Remove README.md file + command: rm README.md + working_directory: << parameters.working_directory >> + - find_and_replace: + files: package.json android/sourcemaps.gradle ios/sourcemaps.sh + search: instabug-reactnative + replace: << parameters.npm_package >> + working_directory: << parameters.working_directory >> + - find_and_replace: + files: cli/upload/uploadSourcemaps.ts cli/upload/uploadSoFiles.ts + search: api.instabug.com + replace: << parameters.api_endpoint >> + working_directory: << parameters.working_directory >> + - find_and_replace: + files: android/native.gradle + search: 'com.instabug.library:instabug:' + replace: 'com.instabug.library-<< parameters.android_package >>:instabug:' + working_directory: << parameters.working_directory >> + + prepare_dream11: + parameters: + working_directory: + type: string + default: ~/project + steps: + - install_node_modules + - prepare_custom_package: + npm_package: '@instabug/instabug-reactnative-dream11' + android_package: dream11 + api_endpoint: st001012dream11.instabug.com + working_directory: << parameters.working_directory >> + - run: + name: Give execute permission to Dream11 script + command: chmod +x ./scripts/dream-11-delete-unused-features.sh + working_directory: << parameters.working_directory >> + - run: + name: Remove unused features + command: ./scripts/dream-11-delete-unused-features.sh + working_directory: << parameters.working_directory >> + # Remove the NDK package to avoid dependency conflicts on Android. + - run: + name: Remove the NDK package + command: yarn remove instabug-reactnative-ndk + working_directory: << parameters.working_directory >>/examples/default notify_github: parameters: @@ -101,20 +182,38 @@ jobs: command: yarn lint:ci test_module: + parameters: + prepare_steps: + type: steps + default: [] + collect_coverage: + type: boolean + default: false executor: name: node/default steps: - advanced-checkout/shallow-checkout + - steps: << parameters.prepare_steps >> - install_node_modules - run: name: Run Tests command: yarn test - - persist_to_workspace: - root: coverage - paths: - - lcov.info + - when: + condition: << parameters.collect_coverage >> + steps: + - persist_to_workspace: + root: coverage + paths: + - lcov.info test_android: + parameters: + prepare_steps: + type: steps + default: [] + collect_coverage: + type: boolean + default: false executor: name: android/android-machine tag: '2024.01.1' @@ -123,15 +222,18 @@ jobs: INSTABUG_SOURCEMAPS_UPLOAD_DISABLE: true steps: - advanced-checkout/shallow-checkout - # - node/install-yarn + - steps: << parameters.prepare_steps >> - install_node_modules - android/run-tests: working-directory: android test-command: ./gradlew test - - persist_to_workspace: - root: ~/project/android/build/reports/jacoco/jacocoTestReport - paths: - - jacocoTestReport.xml + - when: + condition: << parameters.collect_coverage >> + steps: + - persist_to_workspace: + root: ~/project/android/build/reports/jacoco/jacocoTestReport + paths: + - jacocoTestReport.xml validate_shell_files: machine: @@ -160,10 +262,18 @@ jobs: app-dir: examples/default - install_pods: working_directory: examples/default/ios + deployment: true - run: git --no-pager diff - run: git diff-index HEAD --exit-code -p -I EXCLUDED_ARCHS # Ignore Arch Changes test_ios: + parameters: + prepare_steps: + type: steps + default: [] + collect_coverage: + type: boolean + default: false macos: xcode: 13.4.1 resource_class: macos.m1.medium.gen1 @@ -172,6 +282,7 @@ jobs: INSTABUG_SOURCEMAPS_UPLOAD_DISABLE: true steps: - advanced-checkout/shallow-checkout + - steps: << parameters.prepare_steps >> - install_node_modules - install_pods: working_directory: ios @@ -186,16 +297,23 @@ jobs: -sdk iphonesimulator \ -destination 'platform=iOS Simulator,name=iPhone 13 Pro Max,OS=15.5' \ test | xcpretty - - run: - name: Convert xcresult into JSON report - working_directory: ios/coverage - command: xcrun xccov view --report --json result.xcresult > xcode.json - - persist_to_workspace: - root: ios/coverage - paths: - - xcode.json + - when: + condition: << parameters.collect_coverage >> + steps: + - run: + name: Convert xcresult into JSON report + working_directory: ios/coverage + command: xcrun xccov view --report --json result.xcresult > xcode.json + - persist_to_workspace: + root: ios/coverage + paths: + - xcode.json e2e_ios: + parameters: + prepare_steps: + type: steps + default: [] macos: xcode: 13.4.1 resource_class: macos.m1.medium.gen1 @@ -203,6 +321,7 @@ jobs: INSTABUG_SOURCEMAPS_UPLOAD_DISABLE: true steps: - advanced-checkout/shallow-checkout + - steps: << parameters.prepare_steps >> - install_node_modules - node/install-packages: pkg-manager: yarn @@ -219,6 +338,11 @@ jobs: name: Rebuild Detox.framework Cache working_directory: examples/default command: detox clean-framework-cache && detox build-framework-cache + # This is a workaround until the iOS SDK is updated to prioritize the custom + # Instabug.plist over the internal Config.plist. + - run: + name: Customize API Endpoints + command: ./scripts/customize-ios-endpoints.sh - run: name: Detox - Build Release App working_directory: examples/default @@ -229,6 +353,10 @@ jobs: command: detox test --configuration ios.sim.release --cleanup e2e_android: + parameters: + prepare_steps: + type: steps + default: [] executor: name: android/android-machine tag: '2024.01.1' @@ -237,7 +365,7 @@ jobs: INSTABUG_SOURCEMAPS_UPLOAD_DISABLE: true steps: - advanced-checkout/shallow-checkout - # - node/install-yarn + - steps: << parameters.prepare_steps >> - install_node_modules - node/install-packages: pkg-manager: yarn @@ -264,33 +392,16 @@ jobs: release_custom_package: parameters: - npm_package: - type: string - android_package: - type: string - api_endpoint: - type: string + prepare_steps: + type: steps + default: [] working_directory: ~/project executor: name: node/default steps: - advanced-checkout/shallow-checkout + - steps: << parameters.prepare_steps >> - install_node_modules - - run: - name: Remove README.md file - command: rm README.md - - find_and_replace: - files: package.json android/sourcemaps.gradle ios/sourcemaps.sh - search: instabug-reactnative - replace: << parameters.npm_package >> - - find_and_replace: - files: cli/UploadSourcemaps.ts cli/UploadSoFiles.ts - search: api.instabug.com - replace: << parameters.api_endpoint >> - - find_and_replace: - files: android/native.gradle - search: 'com.instabug.library:instabug:' - replace: 'com.instabug.library-<< parameters.android_package >>:instabug:' - run: name: Build the SDK command: yarn build @@ -301,45 +412,6 @@ jobs: name: Publish new enterprise version command: npm publish - # Automate the enterprise D11 sdk changes - release_d11: - working_directory: ~/project - executor: - name: node/default - steps: - - advanced-checkout/shallow-checkout - - install_node_modules - - run: - name: Remove README.md file - command: rm README.md - - find_and_replace: - files: package.json ios/sourcemaps.sh android/sourcemaps.gradle - search: instabug-reactnative - replace: '@instabug/instabug-reactnative-dream11' - - find_and_replace: - files: cli/UploadSourcemaps.ts cli/UploadSoFiles.ts - search: api.instabug.com - replace: st001012dream11.instabug.com - - find_and_replace: - files: android/native.gradle - search: com.instabug.library:instabug - replace: 'com.instabug.library-dream11:instabug' - - run: - name: give exec permssion to d11 script - command: chmod +x ./scripts/dream-11-delete-unused-features.sh - - run: - name: remove un-required features - command: ./scripts/dream-11-delete-unused-features.sh - - run: - name: Build the SDK - command: yarn build - - run: - name: Authorize with npm - command: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc - - run: - name: Publish new enterprise version - command: npm publish - publish: macos: xcode: 13.4.1 @@ -397,35 +469,22 @@ jobs: - run: yarn build - run: yarn remove @instabug/danger-plugin-coverage - run: - name: Remove build files from .gitignore - command: sed -i '/dist/d' .gitignore && sed -i '/bin/d' .gitignore - - run: - name: Get snapshot branch name + name: Get snapshot version command: | - source scripts/snapshot-branch.sh - echo "export SNAPSHOT_BRANCH=$SNAPSHOT_BRANCH" >> "$BASH_ENV" + source scripts/snapshot-version.sh + echo "export SNAPSHOT_VERSION=$SNAPSHOT_VERSION" >> "$BASH_ENV" - run: - name: Setup Git - command: | - git config --global user.name "Instabug-CP-CI" - git config --global user.email github-ci+cp@instabug.com - - run: - name: Create snapshot branch - command: git checkout -b $SNAPSHOT_BRANCH - - run: - name: Commit changes - command: | - git add . - git commit -m "chore: add generate files" - - run: - name: Push snapshot - command: git push --force origin $SNAPSHOT_BRANCH + name: Authorize with NPM + command: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc + - run: npm version $SNAPSHOT_VERSION --no-git-tag-version + - run: npm publish --tag snapshot - run: name: Install jq command: sudo apt-get update && sudo apt-get install -y jq - - run: - name: Replace snapshot branch in comment template - command: sed -i "s|{BRANCH}|$SNAPSHOT_BRANCH|g" scripts/snapshot-comment.md + - find_and_replace: + files: scripts/snapshot-comment.md + search: '{VERSION}' + replace: $SNAPSHOT_VERSION - notify_github: data: "$(jq -Rcs '{ body: . }' scripts/snapshot-comment.md)" @@ -438,11 +497,14 @@ workflows: - test_android - test_ios - lint - - test_module - - test_android + - test_module: + collect_coverage: true + - test_android: + collect_coverage: true + - test_ios: + collect_coverage: true - validate_shell_files - sync_generated_files - - test_ios - e2e_ios - e2e_android - hold_generate_snapshot: @@ -463,12 +525,6 @@ workflows: filters: branches: only: master - - hold_release_d11: - requires: *release_dependencies - type: approval - filters: - branches: - only: dream11 - publish: requires: - hold_publish @@ -481,6 +537,7 @@ workflows: filters: branches: only: master + - hold_release_nn: requires: *release_dependencies type: approval @@ -494,9 +551,12 @@ workflows: filters: branches: only: master - npm_package: '@instabug/react-native-nn' - android_package: nn - api_endpoint: st001009nn.instabug.com + prepare_steps: + - prepare_custom_package: + npm_package: '@instabug/react-native-nn' + android_package: nn + api_endpoint: st001009nn.instabug.com + - hold_release_injazat: requires: *release_dependencies type: approval @@ -510,12 +570,49 @@ workflows: filters: branches: only: master - npm_package: '@instabug/react-native-injazat' - android_package: injazat - api_endpoint: st001013mec1.instabug.com - - release_d11: + prepare_steps: + - prepare_custom_package: + npm_package: '@instabug/react-native-injazat' + android_package: injazat + api_endpoint: st001013mec1.instabug.com + + # Dream11 tests + - hold_test_dream11: + type: approval + - test_module: + name: test_module_dream11 + <<: *dream11_prepare_config + - test_android: + name: test_android_dream11 + <<: *dream11_prepare_config + - test_ios: + name: test_ios_dream11 + <<: *dream11_prepare_config + - e2e_android: + name: e2e_android_dream11 + <<: *dream11_prepare_config + - e2e_ios: + name: e2e_ios_dream11 + <<: *dream11_prepare_config + + # Dream11 release + - hold_release_dream11: + requires: + - test_module_dream11 + - test_android_dream11 + - test_ios_dream11 + - e2e_android_dream11 + - e2e_ios_dream11 + type: approval + filters: + branches: + only: dream11 + - release_custom_package: + name: release_dream11 requires: - - hold_release_d11 + - hold_release_dream11 filters: branches: only: dream11 + prepare_steps: + - prepare_dream11 diff --git a/.gitignore b/.gitignore index 50fb3439f..4033aaa3b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ # Distribution dist/ bin/ +upload/* +!upload/package.json # NodeJS node_modules/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 2287f6614..b050ce8c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,64 @@ # Changelog +## [Unreleased](https://github.com/Instabug/Instabug-React-Native/compare/v13.3.0...dev) + +### Added + +- Add support for Expo Router navigation tracking ([#1270](https://github.com/Instabug/Instabug-React-Native/pull/1270)). +- Enhance the network interceptor to capture more client error messages ([#1257](https://github.com/Instabug/Instabug-React-Native/pull/1257)). + +### Fixed + +- Drop non-error objects reported as crashes since they don't have a stack trace ([#1279](https://github.com/Instabug/Instabug-React-Native/pull/1279)). +- Fix APM network logging on iOS when the response body is missing or empty. ([#1273](https://github.com/Instabug/Instabug-React-Native/pull/1273)). + +## [13.3.0](https://github.com/Instabug/Instabug-React-Native/compare/v13.2.0...v13.3.0) (August 4, 2024) + +### Added + +- Add support for Feature Flags APIs `Instabug.addFeatureFlags`, `Instabug.removeFeatureFlags` and `Instabug.clearAllFeatureFlags` ([#1230](https://github.com/Instabug/Instabug-React-Native/pull/1230)). +- Export `uploadSourcemaps` and `uploadSoFiles` utilities in the `instabug-reactnative/upload` sub-package for usage in custom Node.js upload scripts ([#1252](https://github.com/Instabug/Instabug-React-Native/pull/1252)). + +### Changed + +- Bump Instabug Android SDK to v13.3.0 ([#1261](https://github.com/Instabug/Instabug-React-Native/pull/1261)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v13.3.0). +- Bump Instabug iOS SDK to v13.3.0 ([#1262](https://github.com/Instabug/Instabug-React-Native/pull/1262)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/13.3.0). + +### Deprecated + +- Deprecate Experiments APIs `Instabug.addExperiments`, `Instabug.removeExperiments` and `Instabug.clearAllExperiments` in favor of the new Feature Flags APIs ([#1230](https://github.com/Instabug/Instabug-React-Native/pull/1230)). + +### Fixed + +- Fix APM network logging on Android ([#1253](https://github.com/Instabug/Instabug-React-Native/pull/1253)). +- Fix an OOM (out-of-memory) crash while saving network logs on Android ([#1244](https://github.com/Instabug/Instabug-React-Native/pull/1244)). + +## [13.2.0](https://github.com/Instabug/Instabug-React-Native/compare/v13.1.1...v13.2.0) (July 7, 2024) + +### Changed + +- Bump Instabug Android SDK to v13.2.0 ([#1245](https://github.com/Instabug/Instabug-React-Native/pull/1245)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v13.2.0). +- Bump Instabug iOS SDK to v13.2.0 ([#1246](https://github.com/Instabug/Instabug-React-Native/pull/1246)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/13.2.0). + +### Fixed + +- Change parameters used inside inner classes to `final` in Android code to maintain compatibility with Java 7 and earlier ([#1239](https://github.com/Instabug/Instabug-React-Native/pull/1239)). + +## [13.1.1](https://github.com/Instabug/Instabug-React-Native/compare/v13.0.5...v13.1.1) (June 6, 2024) + +### Added + +- Add support for passing a grouping fingerprint, error level, and user attributes to the `CrashReporting.reportError` non-fatals API ([#1194](https://github.com/Instabug/Instabug-React-Native/pull/1194)). + +### Changed + +- Bump Instabug iOS SDK to v13.1.0 ([#1227](https://github.com/Instabug/Instabug-React-Native/pull/1227)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/13.1.0). +- Bump Instabug android SDK to v13.1.1 ([#1228](https://github.com/Instabug/Instabug-React-Native/pull/1228)). [See release notes](https://github.com/Instabug/android/releases/tag/v13.1.0). + +### Fixed + +- Read `INSTABUG_APP_TOKEN` from system environment when there is no default value ([#1232](https://github.com/Instabug/Instabug-React-Native/pull/1232)). + ## [13.0.5](https://github.com/Instabug/Instabug-React-Native/compare/v13.0.4...v13.0.5) (May 18, 2024) ### Changed @@ -26,17 +85,17 @@ - Bump Instabug iOS SDK to v13.0.0 ([#1189](https://github.com/Instabug/Instabug-React-Native/pull/1189)). [See release notes](https://github.com/instabug/instabug-ios/releases/tag/13.0.0). - Bump Instabug Android SDK to v13.0.0 ([#1188](https://github.com/Instabug/Instabug-React-Native/pull/1188)). [See release notes](https://github.com/Instabug/android/releases/tag/v13.0.0). -## [12.9.0](https://github.com/Instabug/Instabug-React-Native/compare/v12.8.0...v12.9.0) (April 2, 2024) +### Deprecated + +- Deprecate Execution Traces APIs `APM.startExecutionTrace`, `Trace.end` and `Trace.setAttribute` in favor of the new App Flows APIs ([#1138](https://github.com/Instabug/Instabug-React-Native/pull/1138)). + +## [12.9.0](https://github.com/Instabug/Instabug-React-Native/compare/v12.8.0...12.9.0)(April 2, 2024) ### Added - Adds symbol files upload script ([#1137](https://github.com/Instabug/Instabug-React-Native/pull/1137)) - Support enabling NDK crash capturing on Android ([#1132](https://github.com/Instabug/Instabug-React-Native/pull/1132)). -### Deprecated - -- Deprecate Execution Traces APIs `APM.startExecutionTrace`, `Trace.end` and `Trace.setAttribute` in favor of the new App Flows APIs ([#1138](https://github.com/Instabug/Instabug-React-Native/pull/1138)). - ### Changed - Bump Instabug Android SDK to v12.9.0 ([#1168](https://github.com/Instabug/Instabug-React-Native/pull/1168)). [See release notes](https://github.com/Instabug/android/releases/tag/v12.9.0). diff --git a/android/build.gradle b/android/build.gradle index ac49ae2e9..ffd71b5f8 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -57,7 +57,7 @@ android { minSdkVersion getExtOrDefault('minSdkVersion').toInteger() targetSdkVersion getExtOrDefault('targetSdkVersion').toInteger() versionCode 1 - versionName "13.0.5" + versionName "13.3.0" multiDexEnabled true ndk { abiFilters "armeabi-v7a", "x86" diff --git a/android/native.gradle b/android/native.gradle index 93cb37d50..0e86a4edf 100644 --- a/android/native.gradle +++ b/android/native.gradle @@ -1,5 +1,5 @@ project.ext.instabug = [ - version: '13.0.3' + version: '13.3.0' ] dependencies { diff --git a/android/sourcemaps.gradle b/android/sourcemaps.gradle index f3fe5b59d..37875e070 100644 --- a/android/sourcemaps.gradle +++ b/android/sourcemaps.gradle @@ -91,7 +91,7 @@ String resolveVar(String name, String envKey, String defaultValue) { def env = System.getenv() def envValue = env.get(envKey) - if (envValue != null && envValue != defaultValue) { + if (envValue != null && defaultValue !=null && envValue != defaultValue) { project.logger.warn "Environment variable `${envKey}` might have incorrect value, " + "make sure this was intentional:\n" + " Environment Value: ${envValue}\n" + diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 44ad85bcd..9af27fbd3 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ - diff --git a/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java b/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java index 522bd0dd0..37f730cbe 100644 --- a/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java +++ b/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java @@ -4,6 +4,7 @@ import com.instabug.bug.BugReporting; import com.instabug.bug.invocation.Option; +import com.instabug.crash.models.IBGNonFatalException; import com.instabug.featuresrequest.ActionType; import com.instabug.library.InstabugColorTheme; import com.instabug.library.InstabugCustomTextPlaceHolder.Key; @@ -54,11 +55,19 @@ static Map getAll() { putAll(extendedBugReportStates); putAll(reproModes); putAll(sdkLogLevels); + putAll(nonFatalExceptionLevel); putAll(locales); putAll(placeholders); }}; } + public static ArgsMap nonFatalExceptionLevel = new ArgsMap() {{ + put("nonFatalErrorLevelCritical", IBGNonFatalException.Level.CRITICAL); + put("nonFatalErrorLevelError", IBGNonFatalException.Level.ERROR); + put("nonFatalErrorLevelWarning", IBGNonFatalException.Level.WARNING); + put("nonFatalErrorLevelInfo", IBGNonFatalException.Level.INFO); + }}; + static ArgsMap invocationEvents = new ArgsMap() {{ put("invocationEventNone", InstabugInvocationEvent.NONE); put("invocationEventShake", InstabugInvocationEvent.SHAKE); diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugAPMModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugAPMModule.java index d7662c653..e2ff10b99 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugAPMModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugAPMModule.java @@ -5,6 +5,8 @@ import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; @@ -12,6 +14,7 @@ import com.instabug.apm.APM; import com.instabug.apm.model.ExecutionTrace; import com.instabug.apm.networking.APMNetworkLogger; +import com.instabug.apm.networkinterception.cp.APMCPNetworkLog; import com.instabug.reactlibrary.utils.MainThreadHandler; import org.json.JSONException; @@ -312,56 +315,59 @@ public void run() { }); } - /** - * Send Apm network log by Reflection - */ @ReactMethod - public void networkLog(String networkData) throws JSONException { - try{ - APMNetworkLogger apmNetworkLogger = new APMNetworkLogger(); - JSONObject jsonObject = new JSONObject(networkData); - final String requestUrl = (String) jsonObject.get("url"); - final String requestBody = (String) jsonObject.get("requestBody"); - final String responseBody = (String) jsonObject.get("responseBody"); - final String requestMethod = (String) jsonObject.get("method"); - //-------------------------------------------- - final String requestContentType = (String) jsonObject.get("requestContentType"); - final String responseContentType = (String) jsonObject.get("contentType"); - //-------------------------------------------- - final long requestBodySize = ((Number) jsonObject.get("requestBodySize")).longValue(); - final long responseBodySize = ((Number) jsonObject.get("responseBodySize")).longValue(); - //-------------------------------------------- - final String errorDomain = (String) jsonObject.get("errorDomain"); - final Integer statusCode = (Integer) jsonObject.get("responseCode"); - final long requestDuration = ((Number) jsonObject.get("duration")).longValue(); - final long requestStartTime = ((Number) jsonObject.get("startTime")).longValue() * 1000; - final String requestHeaders = (String) jsonObject.get("requestHeaders").toString(); - final String responseHeaders = (String) jsonObject.get("responseHeaders").toString(); - final String errorMessage; - if(errorDomain.equals("")) { - errorMessage = null; - } else { - errorMessage = errorDomain; - } - //-------------------------------------------- - String gqlQueryName = null; - if(jsonObject.has("gqlQueryName")){ - gqlQueryName = (String) jsonObject.get("gqlQueryName"); - } - final String serverErrorMessage = (String) jsonObject.get("serverErrorMessage"); + private void networkLogAndroid(final double requestStartTime, + final double requestDuration, + final String requestHeaders, + final String requestBody, + final double requestBodySize, + final String requestMethod, + final String requestUrl, + final String requestContentType, + final String responseHeaders, + final String responseBody, + final double responseBodySize, + final double statusCode, + final String responseContentType, + @Nullable final String errorDomain, + @Nullable final String gqlQueryName, + @Nullable final String serverErrorMessage) { + try { + APMNetworkLogger networkLogger = new APMNetworkLogger(); + + final boolean hasError = errorDomain != null && !errorDomain.isEmpty(); + final String errorMessage = hasError ? errorDomain : null; try { - 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); + 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, APMCPNetworkLog.W3CExternalTraceAttributes.class); if (method != null) { - method.invoke(apmNetworkLogger, requestStartTime, requestDuration, requestHeaders, requestBody, requestBodySize, requestMethod, requestUrl, requestContentType, responseHeaders, responseBody, responseBodySize, statusCode, responseContentType, errorMessage, gqlQueryName, serverErrorMessage); + method.invoke( + networkLogger, + (long) requestStartTime * 1000, + (long) requestDuration, + requestHeaders, + requestBody, + (long) requestBodySize, + requestMethod, + requestUrl, + requestContentType, + responseHeaders, + responseBody, + (long)responseBodySize, + (int) statusCode, + responseContentType, + errorMessage, + gqlQueryName, + serverErrorMessage, + null + ); } else { - Log.e("IB-CP-Bridge", "apmNetworkLogByReflection was not found by reflection"); + Log.e("IB-CP-Bridge", "APMNetworkLogger.log was not found by reflection"); } } catch (Throwable e) { e.printStackTrace(); } - } - catch(Throwable e) { + } catch(Throwable e) { e.printStackTrace(); } } diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugCrashReportingModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugCrashReportingModule.java index 4bd84c0bd..f7c076253 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugCrashReportingModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugCrashReportingModule.java @@ -2,11 +2,15 @@ import static com.instabug.reactlibrary.utils.InstabugUtil.getMethod; +import androidx.annotation.NonNull; + 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.ReadableMap; import com.instabug.crash.CrashReporting; +import com.instabug.crash.models.IBGNonFatalException; import com.instabug.library.Feature; import com.instabug.reactlibrary.utils.MainThreadHandler; @@ -14,6 +18,8 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -57,8 +63,8 @@ public void run() { * Send unhandled JS error object * * @param exceptionObject Exception object to be sent to Instabug's servers - * @param promise This makes sure that the RN side crashes the app only after the Android SDK - * finishes processing/handling the crash. + * @param promise This makes sure that the RN side crashes the app only after the Android SDK + * finishes processing/handling the crash. */ @ReactMethod public void sendJSCrash(final String exceptionObject, final Promise promise) { @@ -79,41 +85,64 @@ public void run() { * Send handled JS error object * * @param exceptionObject Exception object to be sent to Instabug's servers + * @param userAttributes (Optional) extra user attributes attached to the crash + * @param fingerprint (Optional) key used to customize how crashes are grouped together + * @param level different severity levels for errors */ @ReactMethod - public void sendHandledJSCrash(final String exceptionObject) { + public void sendHandledJSCrash(final String exceptionObject, @Nullable final ReadableMap userAttributes, @Nullable final String fingerprint, @Nullable final String level) { try { - JSONObject jsonObject = new JSONObject(exceptionObject); - sendJSCrashByReflection(jsonObject, true, null); - } catch (Exception e) { + final JSONObject jsonObject = new JSONObject(exceptionObject); + MainThreadHandler.runOnMainThread(new Runnable() { + @Override + public void run() { + try { + Method method = getMethod(Class.forName("com.instabug.crash.CrashReporting"), "reportException", JSONObject.class, boolean.class, + Map.class, JSONObject.class, IBGNonFatalException.Level.class); + if (method != null) { + IBGNonFatalException.Level nonFatalExceptionLevel = ArgsRegistry.nonFatalExceptionLevel.getOrDefault(level, IBGNonFatalException.Level.ERROR); + Map userAttributesMap = userAttributes == null ? null : userAttributes.toHashMap(); + JSONObject fingerprintObj = fingerprint == null ? null : CrashReporting.getFingerprintObject(fingerprint); + + method.invoke(null, jsonObject, true, userAttributesMap, fingerprintObj, nonFatalExceptionLevel); + + RNInstabugReactnativeModule.clearCurrentReport(); + } + } catch (ClassNotFoundException | IllegalAccessException | + InvocationTargetException e) { + e.printStackTrace(); + } + } + }); + } catch (Throwable e) { e.printStackTrace(); } } - private void sendJSCrashByReflection(final JSONObject exceptionObject, final boolean isHandled, @Nullable final Runnable onComplete) { - MainThreadHandler.runOnMainThread(new Runnable() { - @Override - public void run() { - try { - Method method = getMethod(Class.forName("com.instabug.crash.CrashReporting"), "reportException", JSONObject.class, boolean.class); - if (method != null) { - method.invoke(null, exceptionObject, isHandled); - RNInstabugReactnativeModule.clearCurrentReport(); - } - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } finally { - if (onComplete != null) { - onComplete.run(); - } - } - } - }); - } + private void sendJSCrashByReflection(final JSONObject exceptionObject, final boolean isHandled, @Nullable final Runnable onComplete) { + MainThreadHandler.runOnMainThread(new Runnable() { + @Override + public void run() { + try { + Method method = getMethod(Class.forName("com.instabug.crash.CrashReporting"), "reportException", JSONObject.class, boolean.class); + if (method != null) { + method.invoke(null, exceptionObject, isHandled); + RNInstabugReactnativeModule.clearCurrentReport(); + } + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } finally { + if (onComplete != null) { + onComplete.run(); + } + } + } + }); + } /** * Enables and disables capturing native C++ NDK crash reporting. diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java index 0cd0a3940..f936eaa12 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java @@ -5,6 +5,7 @@ import android.app.Application; import android.graphics.Bitmap; import android.net.Uri; +import android.util.Log; import android.view.View; import androidx.annotation.UiThread; @@ -15,6 +16,7 @@ import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeArray; @@ -28,17 +30,20 @@ import com.instabug.library.LogLevel; import com.instabug.library.ReproConfigurations; import com.instabug.library.core.InstabugCore; +import com.instabug.library.featuresflags.model.IBGFeatureFlag; import com.instabug.library.internal.module.InstabugLocale; import com.instabug.library.invocation.InstabugInvocationEvent; import com.instabug.library.logging.InstabugLog; import com.instabug.library.model.NetworkLog; import com.instabug.library.model.Report; import com.instabug.library.ui.onboarding.WelcomeMessage; +import com.instabug.library.util.InstabugSDKLogger; import com.instabug.reactlibrary.utils.ArrayUtil; import com.instabug.reactlibrary.utils.EventEmitterModule; import com.instabug.reactlibrary.utils.MainThreadHandler; import com.instabug.reactlibrary.utils.RNTouchedViewExtractor; + import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; @@ -48,6 +53,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -60,7 +66,7 @@ */ public class RNInstabugReactnativeModule extends EventEmitterModule { - private static final String TAG = RNInstabugReactnativeModule.class.getSimpleName(); + private static final String TAG = "IBG-RN-Core"; private InstabugCustomTextPlaceHolder placeHolders; private static Report currentReport; @@ -161,7 +167,7 @@ public void run() { } @ReactMethod - public void setCodePushVersion(@Nullable String version) { + public void setCodePushVersion(@Nullable final String version) { MainThreadHandler.runOnMainThread(new Runnable() { @Override public void run() { @@ -895,27 +901,38 @@ public void run() { }); } - /** - * Extracts HTTP connection properties. Request method, Headers, Date, Url and Response code - * - * @param jsonObject the JSON object containing all HTTP connection properties - * @throws JSONException - */ @ReactMethod - public void networkLog(String jsonObject) throws JSONException { - NetworkLog networkLog = new NetworkLog(); - String date = System.currentTimeMillis()+""; - networkLog.setDate(date); - JSONObject newJSONObject = new JSONObject(jsonObject); - networkLog.setUrl(newJSONObject.getString("url")); - networkLog.setRequest(newJSONObject.getString("requestBody")); - networkLog.setResponse(newJSONObject.getString("responseBody")); - networkLog.setMethod(newJSONObject.getString("method")); - networkLog.setResponseCode(newJSONObject.getInt("responseCode")); - networkLog.setRequestHeaders(newJSONObject.getString("requestHeaders")); - networkLog.setResponseHeaders(newJSONObject.getString("responseHeaders")); - networkLog.setTotalDuration(newJSONObject.getLong("duration")); - networkLog.insert(); + public void networkLogAndroid(final String url, + final String requestBody, + final String responseBody, + final String method, + final double responseCode, + final String requestHeaders, + final String responseHeaders, + final double duration) { + try { + final String date = String.valueOf(System.currentTimeMillis()); + + NetworkLog networkLog = new NetworkLog(); + networkLog.setDate(date); + networkLog.setUrl(url); + networkLog.setMethod(method); + networkLog.setResponseCode((int) responseCode); + networkLog.setTotalDuration((long) duration); + + try { + networkLog.setRequest(requestBody); + networkLog.setResponse(responseBody); + networkLog.setRequestHeaders(requestHeaders); + networkLog.setResponseHeaders(responseHeaders); + } catch (OutOfMemoryError | Exception exception) { + Log.d(TAG, "Error: " + exception.getMessage() + "while trying to set network log contents (request body, response body, request headers, and response headers)."); + } + + networkLog.insert(); + } catch (OutOfMemoryError | Exception exception) { + Log.d(TAG, "Error: " + exception.getMessage() + "while trying to insert a network log"); + } } @UiThread @@ -1010,6 +1027,9 @@ public void run() { }); } + /** + * @deprecated see {@link #addFeatureFlags(ReadableArray)} + */ @ReactMethod public void addExperiments(final ReadableArray experiments) { MainThreadHandler.runOnMainThread(new Runnable() { @@ -1026,6 +1046,9 @@ public void run() { }); } + /** + * @deprecated see {@link #removeFeatureFlags(ReadableArray)} + */ @ReactMethod public void removeExperiments(final ReadableArray experiments) { MainThreadHandler.runOnMainThread(new Runnable() { @@ -1042,6 +1065,9 @@ public void run() { }); } + /** + * @deprecated see {@link #removeAllFeatureFlags()} + */ @ReactMethod public void clearAllExperiments() { MainThreadHandler.runOnMainThread(new Runnable() { @@ -1056,6 +1082,59 @@ public void run() { }); } + @ReactMethod + public void addFeatureFlags(final ReadableMap featureFlagsMap) { + MainThreadHandler.runOnMainThread(new Runnable() { + @Override + public void run() { + try { + Iterator> iterator = featureFlagsMap.getEntryIterator(); + ArrayList featureFlags = new ArrayList<>(); + while (iterator.hasNext()) { + Map.Entry item = iterator.next(); + String variant = (String) item.getValue(); + String name = item.getKey(); + featureFlags.add(new IBGFeatureFlag(name, variant.isEmpty() ? null : variant)); + } + if (!featureFlags.isEmpty()) { + Instabug.addFeatureFlags(featureFlags); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + @ReactMethod + public void removeFeatureFlags(final ReadableArray featureFlags) { + MainThreadHandler.runOnMainThread(new Runnable() { + @Override + public void run() { + try { + ArrayList stringArray = ArrayUtil.parseReadableArrayOfStrings(featureFlags); + Instabug.removeFeatureFlag(stringArray); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + @ReactMethod + public void removeAllFeatureFlags() { + MainThreadHandler.runOnMainThread(new Runnable() { + @Override + public void run() { + try { + Instabug.removeAllFeatureFlags(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + @ReactMethod public void willRedirectToStore() { MainThreadHandler.runOnMainThread(new Runnable() { diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java index 1c3967502..5024c6180 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java @@ -83,7 +83,7 @@ public void run() { } @ReactMethod - public void getSessionReplayLink(Promise promise) { + public void getSessionReplayLink(final Promise promise) { MainThreadHandler.runOnMainThread(new Runnable() { @Override public void run() { diff --git a/android/src/main/java/com/instabug/reactlibrary/utils/ReportUtil.java b/android/src/main/java/com/instabug/reactlibrary/utils/ReportUtil.java index a44da1882..d33387e7d 100644 --- a/android/src/main/java/com/instabug/reactlibrary/utils/ReportUtil.java +++ b/android/src/main/java/com/instabug/reactlibrary/utils/ReportUtil.java @@ -64,18 +64,4 @@ public static Report createReport(ReadableArray tags, ReadableArray consoleLogs, return report; } - public static WritableArray parseConsoleLogs(ArrayList consoleLogs) { - WritableArray writableArray = new WritableNativeArray(); - - for(int i = 0; i < consoleLogs.size(); i++) { - try { - writableArray.pushString(consoleLogs.get(i).toJson()); - } catch (Exception e) { - e.printStackTrace(); - } - - } - - return writableArray; - } } diff --git a/android/src/test/java/com/instabug/reactlibrary/RNInstabugCrashReportingModuleTest.java b/android/src/test/java/com/instabug/reactlibrary/RNInstabugCrashReportingModuleTest.java index fd2a9b58e..b5d97985b 100644 --- a/android/src/test/java/com/instabug/reactlibrary/RNInstabugCrashReportingModuleTest.java +++ b/android/src/test/java/com/instabug/reactlibrary/RNInstabugCrashReportingModuleTest.java @@ -1,16 +1,25 @@ package com.instabug.reactlibrary; +import static com.instabug.crash.CrashReporting.getFingerprintObject; +import static com.instabug.reactlibrary.util.GlobalMocks.reflected; +import static org.mockito.AdditionalMatchers.cmpEq; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; import android.os.Looper; import com.instabug.crash.CrashReporting; +import com.instabug.crash.models.IBGNonFatalException; import com.instabug.library.Feature; import com.instabug.reactlibrary.util.GlobalMocks; +import com.instabug.reactlibrary.util.MockReflected; import com.instabug.reactlibrary.utils.MainThreadHandler; +import org.json.JSONException; +import org.json.JSONObject; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -19,6 +28,9 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import java.util.HashMap; +import java.util.Map; + public class RNInstabugCrashReportingModuleTest { private final RNInstabugCrashReportingModule rnModule = new RNInstabugCrashReportingModule(null); @@ -38,6 +50,7 @@ public void mockMainThreadHandler() throws Exception { // Mock Looper class Looper mockMainThreadLooper = mock(Looper.class); Mockito.when(Looper.getMainLooper()).thenReturn(mockMainThreadLooper); + GlobalMocks.setUp(); // Override runOnMainThread @@ -58,6 +71,8 @@ public void tearDown() { mockLooper.close(); mockMainThreadHandler.close(); mockCrashReporting.close(); + GlobalMocks.close(); + } /********Crashes*********/ @@ -80,6 +95,18 @@ public void testSetNDKCrashesEnabledGivenFalse() { mockCrashReporting.verify(() -> CrashReporting.setNDKCrashesState(Feature.State.DISABLED)); } + @Test + public void testSendNonFatalError() { + String jsonCrash = "{}"; + boolean isHandled = true; + String fingerPrint = "test"; + String level = ArgsRegistry.nonFatalExceptionLevel.keySet().iterator().next(); + JSONObject expectedFingerprint = getFingerprintObject(fingerPrint); + IBGNonFatalException.Level expectedLevel = ArgsRegistry.nonFatalExceptionLevel.get(level); + rnModule.sendHandledJSCrash(jsonCrash, null, fingerPrint, level); + reflected.verify(() -> MockReflected.reportException(any(JSONObject.class), eq(isHandled), eq(null), eq(expectedFingerprint), eq(expectedLevel))); + } + @Test public void givenString$sendHandledJSCrash_whenQuery_thenShouldCallNativeApiWithArgs() throws Exception { // JSONObject json = mock(JSONObject.class); diff --git a/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java b/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java index beee65c4b..b9bf2308c 100644 --- a/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java +++ b/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java @@ -18,6 +18,7 @@ import com.instabug.library.IssueType; import com.instabug.library.ReproConfigurations; import com.instabug.library.ReproMode; +import com.instabug.library.featuresflags.model.IBGFeatureFlag; import com.instabug.library.internal.module.InstabugLocale; import com.instabug.library.ui.onboarding.WelcomeMessage; import com.instabug.reactlibrary.utils.MainThreadHandler; @@ -38,6 +39,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -567,8 +569,6 @@ public void testIdentifyUserWithId() { @Test public void given$clearAllExperiments_whenQuery_thenShouldCallNativeApi() { - // given - // when rnModule.clearAllExperiments(); @@ -577,6 +577,56 @@ public void testIdentifyUserWithId() { Instabug.clearAllExperiments(); } + @Test + public void testAddFeatureFlags() { + // given + JavaOnlyMap map = new JavaOnlyMap(); + map.putString("key1", "value1"); + map.putString("key2", "value2"); + + // when + rnModule.addFeatureFlags(map); + + // then + Iterator> iterator = map.getEntryIterator(); + ArrayList featureFlags = new ArrayList<>(); + while (iterator.hasNext()) { + Map.Entry item = iterator.next(); + featureFlags.add(new IBGFeatureFlag(item.getKey(), (String) item.getValue())); + } + + mockInstabug.verify(() -> Instabug.addFeatureFlags(featureFlags)); + + } + + @Test + public void testRemoveFeatureFlags() { + // given + JavaOnlyArray array = new JavaOnlyArray(); + array.pushString("exp1"); + array.pushString("exp2"); + + // when + rnModule.removeFeatureFlags(array); + + // then + List expectedList = new ArrayList(); + for (Object o : array.toArrayList()) { + expectedList.add((String) o); + } + mockInstabug.verify(() -> Instabug.removeFeatureFlag(expectedList)); + + } + + @Test + public void testRemoveAllFeatureFlags() { + // when + rnModule.removeAllFeatureFlags(); + + // then + mockInstabug.verify(() -> Instabug.removeAllFeatureFlags()); + } + @Test public void testWillRedirectToStore() { // when diff --git a/android/src/test/java/com/instabug/reactlibrary/util/GlobalMocks.java b/android/src/test/java/com/instabug/reactlibrary/util/GlobalMocks.java index e4810c405..5b88d6dbc 100644 --- a/android/src/test/java/com/instabug/reactlibrary/util/GlobalMocks.java +++ b/android/src/test/java/com/instabug/reactlibrary/util/GlobalMocks.java @@ -4,8 +4,10 @@ import android.util.Log; +import com.instabug.crash.models.IBGNonFatalException; import com.instabug.reactlibrary.utils.InstabugUtil; +import org.json.JSONObject; import org.mockito.MockedStatic; import java.lang.reflect.Method; @@ -37,6 +39,14 @@ public static void setUp() throws NoSuchMethodException { reflection .when(() -> InstabugUtil.getMethod(Class.forName("com.instabug.library.util.InstabugDeprecationLogger"), "setBaseUrl", String.class)) .thenReturn(mSetBaseUrl); + + // reportException mock + Method mCrashReportException = MockReflected.class.getDeclaredMethod("reportException", JSONObject.class, boolean.class, java.util.Map.class, JSONObject.class, IBGNonFatalException.Level.class); + mCrashReportException.setAccessible(true); + reflection + .when(() -> InstabugUtil.getMethod(Class.forName("com.instabug.crash.CrashReporting"), "reportException", JSONObject.class, + boolean.class, java.util.Map.class, JSONObject.class, IBGNonFatalException.Level.class)) + .thenReturn(mCrashReportException); } public static void close() { diff --git a/android/src/test/java/com/instabug/reactlibrary/util/MockReflected.java b/android/src/test/java/com/instabug/reactlibrary/util/MockReflected.java index bfe886b96..ba1c0386d 100644 --- a/android/src/test/java/com/instabug/reactlibrary/util/MockReflected.java +++ b/android/src/test/java/com/instabug/reactlibrary/util/MockReflected.java @@ -1,5 +1,11 @@ package com.instabug.reactlibrary.util; +import com.instabug.crash.models.IBGNonFatalException; + +import org.json.JSONObject; + +import java.util.Map; + /** * Includes fake implementations of methods called by reflection. * Used to verify whether or not a private methods was called. @@ -16,4 +22,9 @@ public static void setCurrentPlatform(int platform) {} * Instabug.util.InstabugDeprecationLogger.setBaseUrl */ public static void setBaseUrl(String baseUrl) {} + /** + * CrashReporting.reportException + */ + public static void reportException(JSONObject exception, boolean isHandled, Map userAttributes, JSONObject fingerPrint, IBGNonFatalException.Level level) {} + } diff --git a/cli/UploadSoFiles.ts b/cli/UploadSoFiles.ts deleted file mode 100755 index 6329f4d27..000000000 --- a/cli/UploadSoFiles.ts +++ /dev/null @@ -1,88 +0,0 @@ -import axios from 'axios'; -import { Command, Option } from 'commander'; -import FormData from 'form-data'; -import fs from 'fs'; - -interface UploadSoFilesOptions { - arch: 'x86' | 'x86_64' | 'arm64-v8a' | 'armeabi-v7a'; - file: string; - token: string; - name: string; - api_key: string; -} -/** - * This script uploads .so files to the specified endpoint used in NDK crash reporting. - * Usage: node upload-so-files.js --arch --file --api_key --token --name - */ - -export const UploadSoFilesCommand = new Command(); - -UploadSoFilesCommand.name('upload-so-files') - .addOption( - new Option('-arch, --arch ', 'arch') - .choices(['x86', 'x86_64', 'arm64-v8a', 'armeabi-v7a']) - .makeOptionMandatory(), - ) - .addOption( - new Option( - '-f, --file ', - 'The path of the symbol files in Zip format', - ).makeOptionMandatory(), - ) - .addOption(new Option('--api_key ', 'Your App key').makeOptionMandatory()) - .addOption( - new Option('-t, --token ', 'Your App Token') - .env('INSTABUG_APP_TOKEN') - .makeOptionMandatory(), - ) - .addOption( - new Option('-n, --name ', 'The app version name') - .env('INSTABUG_APP_VERSION_NAME') - .makeOptionMandatory(), - ) - .action(function (this: Command) { - const options = this.opts(); - UploadSoFiles(options); - }) - .showHelpAfterError(); - -const UploadSoFiles = async (opts: UploadSoFilesOptions) => { - const fileName = opts.file; - if (fileName == null) { - console.error('Failed to upload So Files: invalid file path'); - process.exit(1); - } - - if (fs.existsSync(fileName) === false) { - console.error('Failed to upload So Files: File not found'); - process.exit(1); - } - var fileExt = fileName.split('.').pop(); - - if (fileExt !== 'zip') { - console.error('Failed to upload So Files: You can only upload ZIP files'); - process.exit(1); - } - - const fileBlob = fs.readFileSync(opts.file); - - const form = new FormData(); - form.append('app_version', opts.name); - form.append('so_file', fileBlob, fileName); - form.append('application_token', opts.token); - form.append('api_key', opts.api_key); - form.append('arch', opts.arch); - - console.log('Uploading So files...'); - const uploadEndpoint = 'https://api.instabug.com/api/web/public/so_files'; - try { - await axios.post(uploadEndpoint, form, { - headers: form.getHeaders(), - }); - - console.log(`Successfully uploaded So Files for version: ${opts.name} with arch ${opts.arch}`); - } catch (err) { - console.error('Failed to upload So Files:', axios.isAxiosError(err) ? err.response?.data : err); - process.exit(1); - } -}; diff --git a/cli/UploadSourcemaps.ts b/cli/UploadSourcemaps.ts deleted file mode 100755 index 939629cb5..000000000 --- a/cli/UploadSourcemaps.ts +++ /dev/null @@ -1,89 +0,0 @@ -import axios from 'axios'; -import { Command, Option } from 'commander'; -import FormData from 'form-data'; -import fs from 'fs'; - -interface UploadSourcemapsOptions { - platform: 'android' | 'ios'; - file: string; - token: string; - name: string; - code: string; - label?: string; -} - -export const uploadSourcemapsCommand = new Command(); - -uploadSourcemapsCommand - .name('upload-sourcemaps') - .addOption( - new Option('-p, --platform ', 'Platform') - .choices(['ios', 'android']) - .makeOptionMandatory(), - ) - .addOption( - new Option('-f, --file ', 'The path of the source map file').makeOptionMandatory(), - ) - .addOption( - new Option('-t, --token ', 'Your App Token') - .env('INSTABUG_APP_TOKEN') - .makeOptionMandatory(), - ) - .addOption( - new Option('-n, --name ', 'The app version name') - .env('INSTABUG_APP_VERSION_NAME') - .makeOptionMandatory(), - ) - .addOption( - new Option('-c, --code ', 'The app version code') - .env('INSTABUG_APP_VERSION_CODE') - .makeOptionMandatory(), - ) - .addOption( - new Option('-l, --label ', "The CodePush label if it's a CodePush release").env( - 'INSTABUG_APP_VERSION_LABEL', - ), - ) - .action(function (this: Command) { - const options = this.opts(); - uploadSourcemaps(options); - }) - .showHelpAfterError(); - -const uploadSourcemaps = async (opts: UploadSourcemapsOptions) => { - const fileName = `${opts.platform}-sourcemap.json`; - const fileBlob = fs.readFileSync(opts.file); - - const version = { - code: opts.code, - name: opts.name, - codepush: opts.label, - }; - - const form = new FormData(); - form.append('app_version', JSON.stringify(version)); - form.append('symbols_file', fileBlob, fileName); - form.append('application_token', opts.token); - form.append('platform', 'react_native'); - form.append('os', opts.platform); - - console.log('Uploading Source map file...'); - - try { - const response = await axios.post('https://api.instabug.com/api/sdk/v3/symbols_files', form, { - headers: form.getHeaders(), - }); - - const appVersion = version.codepush - ? `${version.name} (${version.code})+codepush:${version.codepush}` - : `${version.name} (${version.code})`; - - console.log(`Successfully uploaded Source maps for version: ${appVersion}`); - console.log(response.data); - } catch (err) { - console.error( - 'Failed to upload source maps:', - axios.isAxiosError(err) ? err.response?.data : err, - ); - } -}; diff --git a/cli/commands/UploadSoFiles.ts b/cli/commands/UploadSoFiles.ts new file mode 100755 index 000000000..99b23e3e4 --- /dev/null +++ b/cli/commands/UploadSoFiles.ts @@ -0,0 +1,38 @@ +import { Command, Option } from 'commander'; +import { uploadSoFiles, UploadSoFilesOptions } from '../upload/uploadSoFiles'; + +/** + * This script uploads .so files to the specified endpoint used in NDK crash reporting. + * Usage: node upload-so-files.js --arch --file --api_key --token --name + */ + +export const UploadSoFilesCommand = new Command(); + +UploadSoFilesCommand.name('upload-so-files') + .addOption( + new Option('-arch, --arch ', 'arch') + .choices(['x86', 'x86_64', 'arm64-v8a', 'armeabi-v7a']) + .makeOptionMandatory(), + ) + .addOption( + new Option( + '-f, --file ', + 'The path of the symbol files in Zip format', + ).makeOptionMandatory(), + ) + .addOption(new Option('--api_key ', 'Your App key').makeOptionMandatory()) + .addOption( + new Option('-t, --token ', 'Your App Token') + .env('INSTABUG_APP_TOKEN') + .makeOptionMandatory(), + ) + .addOption( + new Option('-n, --name ', 'The app version name') + .env('INSTABUG_APP_VERSION_NAME') + .makeOptionMandatory(), + ) + .action(function (this: Command) { + const options = this.opts(); + uploadSoFiles(options); + }) + .showHelpAfterError(); diff --git a/cli/commands/UploadSourcemaps.ts b/cli/commands/UploadSourcemaps.ts new file mode 100755 index 000000000..5199693e7 --- /dev/null +++ b/cli/commands/UploadSourcemaps.ts @@ -0,0 +1,40 @@ +import { Command, Option } from 'commander'; +import { uploadSourcemaps, UploadSourcemapsOptions } from '../upload/uploadSourcemaps'; + +export const uploadSourcemapsCommand = new Command(); + +uploadSourcemapsCommand + .name('upload-sourcemaps') + .addOption( + new Option('-p, --platform ', 'Platform') + .choices(['ios', 'android']) + .makeOptionMandatory(), + ) + .addOption( + new Option('-f, --file ', 'The path of the source map file').makeOptionMandatory(), + ) + .addOption( + new Option('-t, --token ', 'Your App Token') + .env('INSTABUG_APP_TOKEN') + .makeOptionMandatory(), + ) + .addOption( + new Option('-n, --name ', 'The app version name') + .env('INSTABUG_APP_VERSION_NAME') + .makeOptionMandatory(), + ) + .addOption( + new Option('-c, --code ', 'The app version code') + .env('INSTABUG_APP_VERSION_CODE') + .makeOptionMandatory(), + ) + .addOption( + new Option('-l, --label ', "The CodePush label if it's a CodePush release").env( + 'INSTABUG_APP_VERSION_LABEL', + ), + ) + .action(function (this: Command) { + const options = this.opts(); + uploadSourcemaps(options); + }) + .showHelpAfterError(); diff --git a/cli/index.ts b/cli/index.ts index c1731d67a..8df747e75 100644 --- a/cli/index.ts +++ b/cli/index.ts @@ -1,8 +1,8 @@ #!/usr/bin/env node import { Command } from 'commander'; -import { uploadSourcemapsCommand } from './UploadSourcemaps'; -import { UploadSoFilesCommand } from './UploadSoFiles'; +import { uploadSourcemapsCommand } from './commands/UploadSourcemaps'; +import { UploadSoFilesCommand } from './commands/UploadSoFiles'; const program = new Command(); diff --git a/cli/upload/index.ts b/cli/upload/index.ts new file mode 100644 index 000000000..b09f4b243 --- /dev/null +++ b/cli/upload/index.ts @@ -0,0 +1,2 @@ +export * from './uploadSourcemaps'; +export * from './uploadSoFiles'; diff --git a/cli/upload/uploadSoFiles.ts b/cli/upload/uploadSoFiles.ts new file mode 100755 index 000000000..fb25f6063 --- /dev/null +++ b/cli/upload/uploadSoFiles.ts @@ -0,0 +1,112 @@ +import axios from 'axios'; +import FormData from 'form-data'; +import fs from 'fs'; + +export interface UploadSoFilesOptions { + arch: 'x86' | 'x86_64' | 'arm64-v8a' | 'armeabi-v7a'; + file: string; + token: string; + name: string; + api_key: string; + + /** + * Disables logging to the console and prevents process exit on error. + * + * @default false + * */ + silent?: boolean; +} + +/** + * Uploads NDK `.so` files to Instabug. + * + * @param opts Options for the `.so` files upload process. + * @returns A promise that resolves to a boolean indicating whether the upload was successful. + */ +export const uploadSoFiles = async (opts: UploadSoFilesOptions): Promise => { + const fileName = opts.file; + + const validFilePath = assert( + fileName != null, + 'Failed to upload So Files: invalid file path', + opts.silent, + ); + + if (!validFilePath) { + return false; + } + + const fileExists = assert( + fs.existsSync(fileName), + 'Failed to upload So Files: File not found', + opts.silent, + ); + + if (!fileExists) { + return false; + } + + const fileExt = fileName.split('.').pop(); + + const isZipFile = assert( + fileExt === 'zip', + 'Failed to upload So Files: You can only upload ZIP files', + opts.silent, + ); + + if (!isZipFile) { + return false; + } + + const fileBlob = fs.readFileSync(opts.file); + + const form = new FormData(); + form.append('app_version', opts.name); + form.append('so_file', fileBlob, fileName); + form.append('application_token', opts.token); + form.append('api_key', opts.api_key); + form.append('arch', opts.arch); + + if (!opts.silent) { + console.log('Uploading So files...'); + } + + const uploadEndpoint = 'https://api.instabug.com/api/web/public/so_files'; + try { + await axios.post(uploadEndpoint, form, { + headers: form.getHeaders(), + }); + + if (!opts.silent) { + console.log( + `Successfully uploaded So Files for version: ${opts.name} with arch ${opts.arch}`, + ); + } + + return true; + } catch (err) { + if (!opts.silent) { + console.error( + 'Failed to upload So Files:', + axios.isAxiosError(err) ? err.response?.data : err, + ); + + process.exit(1); + } + + return false; + } +}; + +export const assert = (condition: unknown, message: string, silent?: boolean): boolean => { + if (silent) { + return Boolean(condition); + } + + if (!condition) { + console.error(message); + process.exit(1); + } + + return true; +}; diff --git a/cli/upload/uploadSourcemaps.ts b/cli/upload/uploadSourcemaps.ts new file mode 100644 index 000000000..701ce9ead --- /dev/null +++ b/cli/upload/uploadSourcemaps.ts @@ -0,0 +1,73 @@ +import axios from 'axios'; +import FormData from 'form-data'; +import fs from 'fs'; + +export interface UploadSourcemapsOptions { + platform: 'android' | 'ios'; + file: string; + token: string; + name: string; + code: string; + label?: string; + + /** + * Disables logging to the console and prevents process exit on error. + * + * @default false + * */ + silent?: boolean; +} + +/** + * Uploads JavaScript sourcemaps to Instabug. + * + * @param opts Options for the sourcemaps upload process. + * @returns A promise that resolves to a boolean indicating whether the upload was successful. + */ +export const uploadSourcemaps = async (opts: UploadSourcemapsOptions): Promise => { + const fileName = `${opts.platform}-sourcemap.json`; + const fileBlob = fs.readFileSync(opts.file); + + const version = { + code: opts.code, + name: opts.name, + codepush: opts.label, + }; + + const form = new FormData(); + form.append('app_version', JSON.stringify(version)); + form.append('symbols_file', fileBlob, fileName); + form.append('application_token', opts.token); + form.append('platform', 'react_native'); + form.append('os', opts.platform); + + if (!opts.silent) { + console.log('Uploading Source map file...'); + } + + try { + const response = await axios.post('https://api.instabug.com/api/sdk/v3/symbols_files', form, { + headers: form.getHeaders(), + }); + + const appVersion = version.codepush + ? `${version.name} (${version.code})+codepush:${version.codepush}` + : `${version.name} (${version.code})`; + + if (!opts.silent) { + console.log(`Successfully uploaded Source maps for version: ${appVersion}`); + console.log(response.data); + } + + return true; + } catch (err) { + if (!opts.silent) { + console.error( + 'Failed to upload source maps:', + axios.isAxiosError(err) ? err.response?.data : err, + ); + } + + return false; + } +}; diff --git a/examples/default/android/app/build.gradle b/examples/default/android/app/build.gradle index f556607ed..c3d537495 100644 --- a/examples/default/android/app/build.gradle +++ b/examples/default/android/app/build.gradle @@ -171,12 +171,6 @@ dependencies { implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0") - debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") - debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { - exclude group:'com.squareup.okhttp3', module:'okhttp' - } - - debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") if (hermesEnabled.toBoolean()) { implementation("com.facebook.react:hermes-android") } else { diff --git a/examples/default/android/app/src/debug/java/com/instabug/react/example/ReactNativeFlipper.java b/examples/default/android/app/src/debug/java/com/instabug/react/example/ReactNativeFlipper.java deleted file mode 100644 index a769aa68f..000000000 --- a/examples/default/android/app/src/debug/java/com/instabug/react/example/ReactNativeFlipper.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - *

This source code is licensed under the MIT license found in the LICENSE file in the root - * directory of this source tree. - */ -package com.instabug.react.example; - -import android.content.Context; -import com.facebook.flipper.android.AndroidFlipperClient; -import com.facebook.flipper.android.utils.FlipperUtils; -import com.facebook.flipper.core.FlipperClient; -import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; -import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; -import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; -import com.facebook.flipper.plugins.inspector.DescriptorMapping; -import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; -import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; -import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; -import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; -import com.facebook.react.ReactInstanceEventListener; -import com.facebook.react.ReactInstanceManager; -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.modules.network.NetworkingModule; -import okhttp3.OkHttpClient; - -/** - * Class responsible of loading Flipper inside your React Native application. This is the debug - * flavor of it. Here you can add your own plugins and customize the Flipper setup. - */ -public class ReactNativeFlipper { - public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { - if (FlipperUtils.shouldEnableFlipper(context)) { - final FlipperClient client = AndroidFlipperClient.getInstance(context); - - client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); - client.addPlugin(new DatabasesFlipperPlugin(context)); - client.addPlugin(new SharedPreferencesFlipperPlugin(context)); - client.addPlugin(CrashReporterPlugin.getInstance()); - - NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); - NetworkingModule.setCustomClientBuilder( - new NetworkingModule.CustomClientBuilder() { - @Override - public void apply(OkHttpClient.Builder builder) { - builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); - } - }); - client.addPlugin(networkFlipperPlugin); - client.start(); - - // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized - // Hence we run if after all native modules have been initialized - ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); - if (reactContext == null) { - reactInstanceManager.addReactInstanceEventListener( - new ReactInstanceEventListener() { - @Override - public void onReactContextInitialized(ReactContext reactContext) { - reactInstanceManager.removeReactInstanceEventListener(this); - reactContext.runOnNativeModulesQueueThread( - new Runnable() { - @Override - public void run() { - client.addPlugin(new FrescoFlipperPlugin()); - } - }); - } - }); - } else { - client.addPlugin(new FrescoFlipperPlugin()); - } - } - } -} diff --git a/examples/default/android/app/src/main/java/com/instabug/react/example/MainApplication.java b/examples/default/android/app/src/main/java/com/instabug/react/example/MainApplication.java index a988f0a84..a5b32db53 100644 --- a/examples/default/android/app/src/main/java/com/instabug/react/example/MainApplication.java +++ b/examples/default/android/app/src/main/java/com/instabug/react/example/MainApplication.java @@ -56,6 +56,5 @@ public void onCreate() { // If you opted-in for the New Architecture, we load the native entry point for this app. DefaultNewArchitectureEntryPoint.load(); } - ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); } } diff --git a/examples/default/android/app/src/main/java/com/instabug/react/example/RNInstabugExampleReactnativePackage.java b/examples/default/android/app/src/main/java/com/instabug/react/example/RNInstabugExampleReactnativePackage.java index 304ab6870..977afa782 100644 --- a/examples/default/android/app/src/main/java/com/instabug/react/example/RNInstabugExampleReactnativePackage.java +++ b/examples/default/android/app/src/main/java/com/instabug/react/example/RNInstabugExampleReactnativePackage.java @@ -6,14 +6,6 @@ import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; -import com.instabug.reactlibrary.RNInstabugAPMModule; -import com.instabug.reactlibrary.RNInstabugBugReportingModule; -import com.instabug.reactlibrary.RNInstabugCrashReportingModule; -import com.instabug.reactlibrary.RNInstabugFeatureRequestsModule; -import com.instabug.reactlibrary.RNInstabugReactnativeModule; -import com.instabug.reactlibrary.RNInstabugRepliesModule; -import com.instabug.reactlibrary.RNInstabugSessionReplayModule; -import com.instabug.reactlibrary.RNInstabugSurveysModule; import java.util.ArrayList; import java.util.Collections; diff --git a/examples/default/android/app/src/main/res/raw/instabug_config.json b/examples/default/android/app/src/main/res/raw/instabug_config.json new file mode 100644 index 000000000..86b3a7a22 --- /dev/null +++ b/examples/default/android/app/src/main/res/raw/instabug_config.json @@ -0,0 +1,4 @@ +{ + "instabug-domain": "api.instabug.com", + "apm-domain": "api-apm.instabug.com" +} diff --git a/examples/default/android/app/src/release/java/com/instabugexample/ReactNativeFlipper.java b/examples/default/android/app/src/release/java/com/instabugexample/ReactNativeFlipper.java deleted file mode 100644 index 34e4708ab..000000000 --- a/examples/default/android/app/src/release/java/com/instabugexample/ReactNativeFlipper.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - *

This source code is licensed under the MIT license found in the LICENSE file in the root - * directory of this source tree. - */ -package com.instabug.react.example; - -import android.content.Context; -import com.facebook.react.ReactInstanceManager; - -/** - * Class responsible of loading Flipper inside your React Native application. This is the release - * flavor of it so it's empty as we don't want to load Flipper. - */ -public class ReactNativeFlipper { - public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { - // Do nothing as we don't want to initialize Flipper on Release. - } -} diff --git a/examples/default/android/build.gradle b/examples/default/android/build.gradle index 0336ff851..7fa616eb1 100644 --- a/examples/default/android/build.gradle +++ b/examples/default/android/build.gradle @@ -27,5 +27,13 @@ allprojects { maven { url("$rootDir/../node_modules/detox/Detox-android") } + + maven { + credentials { + username System.getenv("DREAM11_MAVEN_USERNAME") + password System.getenv("DREAM11_MAVEN_PASSWORD") + } + url "https://mvn.instabug.com/nexus/repository/dream-11" + } } } diff --git a/examples/default/android/gradle.properties b/examples/default/android/gradle.properties index d1a632d51..6a35482a5 100644 --- a/examples/default/android/gradle.properties +++ b/examples/default/android/gradle.properties @@ -24,9 +24,6 @@ android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=true -# Version of flipper SDK to use with React Native -FLIPPER_VERSION=0.182.0 - # Use this property to specify which architecture you want to build. # You can also override it from the CLI using # ./gradlew -PreactNativeArchitectures=x86_64 diff --git a/examples/default/ios/InstabugExample.xcodeproj/project.pbxproj b/examples/default/ios/InstabugExample.xcodeproj/project.pbxproj index 25a646edd..3b05a8f1e 100644 --- a/examples/default/ios/InstabugExample.xcodeproj/project.pbxproj +++ b/examples/default/ios/InstabugExample.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ CC3DF8932A1DFC9A003E9914 /* InstabugSurveysTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC3DF88B2A1DFC99003E9914 /* InstabugSurveysTests.m */; }; CC3DF8942A1DFC9A003E9914 /* InstabugAPMTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC3DF88C2A1DFC99003E9914 /* InstabugAPMTests.m */; }; CC3DF8952A1DFC9A003E9914 /* IBGConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = CC3DF88D2A1DFC9A003E9914 /* IBGConstants.m */; }; + CC487A9C2C71FCFC0021F680 /* Instabug.plist in Resources */ = {isa = PBXBuildFile; fileRef = CC487A9B2C71FCFC0021F680 /* Instabug.plist */; }; CCF1E4092B022CF20024802D /* RNInstabugTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCF1E4082B022CF20024802D /* RNInstabugTests.m */; }; CD36F4707EA1F435D2CC7A15 /* libPods-InstabugExample-InstabugTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF7A6E02D40E0CEEA833CC4 /* libPods-InstabugExample-InstabugTests.a */; }; F7BF47401EF3A435254C97BB /* libPods-InstabugExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BAED0D0441A708AE2390E153 /* libPods-InstabugExample.a */; }; @@ -52,6 +53,7 @@ 9A3D962AB03F97E25566779F /* Pods-InstabugExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InstabugExample.debug.xcconfig"; path = "Target Support Files/Pods-InstabugExample/Pods-InstabugExample.debug.xcconfig"; sourceTree = ""; }; BAED0D0441A708AE2390E153 /* libPods-InstabugExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-InstabugExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; BD54B44E2DF85672BB2D4DEE /* Pods-InstabugExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InstabugExample.release.xcconfig"; path = "Target Support Files/Pods-InstabugExample/Pods-InstabugExample.release.xcconfig"; sourceTree = ""; }; + BE3328762BDACE030078249A /* IBGCrashReporting+CP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "IBGCrashReporting+CP.h"; sourceTree = ""; }; C3C8C24386310A3120006604 /* CrashReportingExampleModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CrashReportingExampleModule.m; sourceTree = ""; }; C3C8C784EADC037C5A752B94 /* CrashReportingExampleModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CrashReportingExampleModule.h; sourceTree = ""; }; CC3DF8852A1DFC99003E9914 /* InstabugCrashReportingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InstabugCrashReportingTests.m; sourceTree = ""; }; @@ -63,6 +65,7 @@ CC3DF88B2A1DFC99003E9914 /* InstabugSurveysTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InstabugSurveysTests.m; sourceTree = ""; }; CC3DF88C2A1DFC99003E9914 /* InstabugAPMTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InstabugAPMTests.m; sourceTree = ""; }; CC3DF88D2A1DFC9A003E9914 /* IBGConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IBGConstants.m; sourceTree = ""; }; + CC487A9B2C71FCFC0021F680 /* Instabug.plist */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; name = Instabug.plist; path = InstabugExample/Instabug.plist; sourceTree = ""; }; CCF1E4082B022CF20024802D /* RNInstabugTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNInstabugTests.m; sourceTree = ""; }; DBCB1B1D023646D84146C91E /* Pods-InstabugExample-InstabugTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InstabugExample-InstabugTests.release.xcconfig"; path = "Target Support Files/Pods-InstabugExample-InstabugTests/Pods-InstabugExample-InstabugTests.release.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; @@ -91,6 +94,7 @@ 00E356EF1AD99517003FC87E /* InstabugTests */ = { isa = PBXGroup; children = ( + BE3328752BDACE030078249A /* Util */, CC3DF8892A1DFC99003E9914 /* IBGConstants.h */, CC3DF88D2A1DFC9A003E9914 /* IBGConstants.m */, CC3DF88C2A1DFC99003E9914 /* InstabugAPMTests.m */, @@ -118,6 +122,7 @@ 13B07FAE1A68108700A75B9A /* InstabugExample */ = { isa = PBXGroup; children = ( + CC487A9B2C71FCFC0021F680 /* Instabug.plist */, 13B07FAF1A68108700A75B9A /* AppDelegate.h */, 13B07FB01A68108700A75B9A /* AppDelegate.mm */, 13B07FB51A68108700A75B9A /* Images.xcassets */, @@ -181,6 +186,14 @@ path = Pods; sourceTree = ""; }; + BE3328752BDACE030078249A /* Util */ = { + isa = PBXGroup; + children = ( + BE3328762BDACE030078249A /* IBGCrashReporting+CP.h */, + ); + path = Util; + sourceTree = ""; + }; C3C8C1DDCEA91410F27A3683 /* native */ = { isa = PBXGroup; children = ( @@ -286,6 +299,7 @@ buildActionMask = 2147483647; files = ( 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, + CC487A9C2C71FCFC0021F680 /* Instabug.plist in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -412,7 +426,7 @@ name = "[CP-User] [instabug-reactnative] Upload Sourcemap"; runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "#!/bin/sh\n\nmain() {\n # Read environment variables from ios/.xcode.env if it exists\n env_path=\"$PODS_ROOT/../.xcode.env\"\n if [ -f \"$env_path\" ]; then\n source \"$env_path\"\n fi\n\n # Read environment variables from ios/.xcode.env.local if it exists\n local_env_path=\"${ENV_PATH}.local\"\n if [ -f \"$local_env_path\" ]; then\n source \"$local_env_path\"\n fi\n\n if [[ \"$INSTABUG_SOURCEMAPS_UPLOAD_DISABLE\" = true ]]; then\n echo \"[Info] \\`INSTABUG_SOURCEMAPS_UPLOAD_DISABLE\\` was set to true, skipping sourcemaps upload...\"\n exit 0\n fi\n\n if [[ \"$CONFIGURATION\" = \"Debug\" ]]; then\n echo \"[Info] Building in debug mode, skipping sourcemaps upload...\"\n exit 0\n fi\n\n if [[ -z \"$INFOPLIST_FILE\" ]] || [[ -z \"$PROJECT_DIR\" ]]; then\n echo \"[Error] Instabug sourcemaps script must be invoked by Xcode\"\n exit 0\n fi\n\n local source_map_file=$(generate_sourcemaps | tail -n 1)\n\n local js_project_dir=\"$PROJECT_DIR/..\"\n local instabug_dir=$(dirname $(node -p \"require.resolve('instabug-reactnative/package.json')\"))\n local inferred_token=$(cd $js_project_dir && source $instabug_dir/scripts/find-token.sh)\n local app_token=$(resolve_var \"App Token\" \"INSTABUG_APP_TOKEN\" \"$inferred_token\" | tail -n 1)\n\n local inferred_name=$(/usr/libexec/PlistBuddy -c 'print CFBundleShortVersionString' \"$PROJECT_DIR/$INFOPLIST_FILE\")\n local version_name=$(resolve_var \"Version Name\" \"INSTABUG_APP_VERSION_NAME\" \"$inferred_name\" | tail -n 1)\n\n local inferred_code=$(/usr/libexec/PlistBuddy -c 'print CFBundleVersion' \"$PROJECT_DIR/$INFOPLIST_FILE\")\n local version_code=$(resolve_var \"Version Code\" \"INSTABUG_APP_VERSION_CODE\" \"$inferred_code\" | tail -n 1)\n\n node $instabug_dir/bin/index.js upload-sourcemaps \\\n --platform ios \\\n --file $source_map_file \\\n --token $app_token \\\n --name $version_name \\\n --code $version_code\n}\n\ngenerate_sourcemaps() {\n local react_native_dir=$(dirname $(node -p \"require.resolve('react-native/package.json')\"))\n\n # Fixes an issue with react-native prior to v0.67.0\n # For more info: https://github.com/facebook/react-native/issues/32168\n export RN_DIR=$react_native_dir \n\n # Used withing `react-native-xcode.sh` to generate sourcemap file\n export SOURCEMAP_FILE=\"$(pwd)/main.jsbundle.map\";\n\n source \"$react_native_dir/scripts/react-native-xcode.sh\"\n\n if [[ ! -f \"$SOURCEMAP_FILE\" ]]; then\n echo \"[Error] Unable to find source map file at: $SOURCEMAP_FILE\"\n exit 0\n fi\n\n echo $SOURCEMAP_FILE\n}\n\nresolve_var() {\n local name=$1\n local env_key=$2\n local default_value=$3\n\n local env_value=\"${!env_key}\"\n\n if [[ -n \"$env_value\" ]] && [[ \"$env_value\" != default_value ]]; then\n echo \"[Warning] Environment variable \\`$env_key\\` might have incorrect value, make sure this was intentional:\"\n echo \" Environment Value: $env_value\"\n echo \" Default Value: $default_value\"\n fi\n\n local value=\"${env_value:-$default_value}\"\n\n if [[ -z \"$value\" ]]; then\n echo \"[Error] Unable to find $name! Set the environment variable \\`$env_key\\` and try again.\"\n exit 0\n fi\n\n echo $value\n}\n\nmain \"$@\"; exit\n"; + shellScript = "#!/bin/sh\n\nmain() {\n # Read environment variables from ios/.xcode.env if it exists\n env_path=\"$PODS_ROOT/../.xcode.env\"\n if [ -f \"$env_path\" ]; then\n source \"$env_path\"\n fi\n\n # Read environment variables from ios/.xcode.env.local if it exists\n local_env_path=\"${ENV_PATH}.local\"\n if [ -f \"$local_env_path\" ]; then\n source \"$local_env_path\"\n fi\n\n if [[ \"$INSTABUG_SOURCEMAPS_UPLOAD_DISABLE\" = true ]]; then\n echo \"[Info] \\`INSTABUG_SOURCEMAPS_UPLOAD_DISABLE\\` was set to true, skipping sourcemaps upload...\"\n exit 0\n fi\n\n if [[ \"$CONFIGURATION\" = \"Debug\" ]]; then\n echo \"[Info] Building in debug mode, skipping sourcemaps upload...\"\n exit 0\n fi\n\n if [[ -z \"$INFOPLIST_FILE\" ]] || [[ -z \"$PROJECT_DIR\" ]]; then\n echo \"[Error] Instabug sourcemaps script must be invoked by Xcode\"\n exit 0\n fi\n\n local source_map_file=$(generate_sourcemaps | tail -n 1)\n\n local js_project_dir=\"$PROJECT_DIR/..\"\n local instabug_dir=$(dirname $(node -p \"require.resolve('instabug-reactnative/package.json')\"))\n local inferred_token=$(cd $js_project_dir && source $instabug_dir/scripts/find-token.sh)\n local app_token=$(resolve_var \"App Token\" \"INSTABUG_APP_TOKEN\" \"$inferred_token\" | tail -n 1)\n\n local inferred_name=$(/usr/libexec/PlistBuddy -c 'print CFBundleShortVersionString' \"$PROJECT_DIR/$INFOPLIST_FILE\")\n local version_name=$(resolve_var \"Version Name\" \"INSTABUG_APP_VERSION_NAME\" \"$inferred_name\" | tail -n 1)\n\n local inferred_code=$(/usr/libexec/PlistBuddy -c 'print CFBundleVersion' \"$PROJECT_DIR/$INFOPLIST_FILE\")\n local version_code=$(resolve_var \"Version Code\" \"INSTABUG_APP_VERSION_CODE\" \"$inferred_code\" | tail -n 1)\n\n node $instabug_dir/bin/index.js upload-sourcemaps \\\n --platform ios \\\n --file $source_map_file \\\n --token $app_token \\\n --name $version_name \\\n --code $version_code\n}\n\ngenerate_sourcemaps() {\n local react_native_dir=$(dirname $(node -p \"require.resolve('react-native/package.json')\"))\n\n # Fixes an issue with react-native prior to v0.67.0\n # For more info: https://github.com/facebook/react-native/issues/32168\n export RN_DIR=$react_native_dir\n\n # Used withing `react-native-xcode.sh` to generate sourcemap file\n export SOURCEMAP_FILE=\"$(pwd)/main.jsbundle.map\";\n\n source \"$react_native_dir/scripts/react-native-xcode.sh\"\n\n if [[ ! -f \"$SOURCEMAP_FILE\" ]]; then\n echo \"[Error] Unable to find source map file at: $SOURCEMAP_FILE\"\n exit 0\n fi\n\n echo $SOURCEMAP_FILE\n}\n\nresolve_var() {\n local name=$1\n local env_key=$2\n local default_value=$3\n\n local env_value=\"${!env_key}\"\n\n if [[ -n \"$env_value\" ]] && [[ -n \"$default_value\" ]] && [[ \"$env_value\" != default_value ]]; then\n echo \"[Warning] Environment variable \\`$env_key\\` might have incorrect value, make sure this was intentional:\"\n echo \" Environment Value: $env_value\"\n echo \" Default Value: $default_value\"\n fi\n\n local value=\"${env_value:-$default_value}\"\n\n if [[ -z \"$value\" ]]; then\n echo \"[Error] Unable to find $name! Set the environment variable \\`$env_key\\` and try again.\"\n exit 0\n fi\n\n echo $value\n}\n\nmain \"$@\"; exit\n"; }; B77A7BA143DBD17E8AAFD0B4 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; diff --git a/examples/default/ios/InstabugExample/Instabug.plist b/examples/default/ios/InstabugExample/Instabug.plist new file mode 100644 index 0000000000000000000000000000000000000000..24d035f427616a1490762b3e7c6b8861c863c4a4 GIT binary patch literal 157 zcmYc)$jK}&F)+Bq$i&PN=b2Yrl9*JQ?ik=38srlV;{w_70_qthB?ZM+`ud3lnYxJu vxq6vU#d^v4xgbIP;*@OtGUIpwWh9ljRB`aiXaq5U0V5-XW?+TVFscFo;^Zpb literal 0 HcmV?d00001 diff --git a/examples/default/ios/InstabugTests/InstabugCrashReportingTests.m b/examples/default/ios/InstabugTests/InstabugCrashReportingTests.m index 83f682cd3..0d5ac7348 100644 --- a/examples/default/ios/InstabugTests/InstabugCrashReportingTests.m +++ b/examples/default/ios/InstabugTests/InstabugCrashReportingTests.m @@ -1,15 +1,21 @@ #import #import "Instabug/Instabug.h" #import "InstabugCrashReportingBridge.h" +#import "OCMock/OCMock.h" +#import "Util/IBGCrashReporting+CP.h" @interface InstabugCrashReportingTests : XCTestCase @property (nonatomic, retain) InstabugCrashReportingBridge *bridge; +@property (nonatomic, strong) id mCrashReporting; + @end @implementation InstabugCrashReportingTests - (void)setUp { self.bridge = [[InstabugCrashReportingBridge alloc] init]; + self.mCrashReporting = OCMClassMock([IBGCrashReporting class]); + } - (void)testSetEnabled { @@ -20,4 +26,22 @@ - (void)testSetEnabled { XCTAssertFalse(IBGCrashReporting.enabled); } +- (void)testSendNonFatalErrorJsonCrash { + NSDictionary *jsonCrash = @{}; + NSString *fingerPrint = @"fingerprint"; + RCTPromiseResolveBlock resolve = ^(id result) {}; + RCTPromiseRejectBlock reject = ^(NSString *code, NSString *message, NSError *error) {}; + NSDictionary *userAttributes = @{ @"key" : @"value", }; + IBGNonFatalLevel ibgNonFatalLevel = IBGNonFatalLevelInfo; + + + [self.bridge sendHandledJSCrash:jsonCrash userAttributes:userAttributes fingerprint:fingerPrint nonFatalExceptionLevel:ibgNonFatalLevel resolver:resolve rejecter:reject]; + + OCMVerify([self.mCrashReporting cp_reportNonFatalCrashWithStackTrace:jsonCrash + level:IBGNonFatalLevelInfo + groupingString:fingerPrint + userAttributes:userAttributes + ]); +} + @end diff --git a/examples/default/ios/InstabugTests/InstabugSampleTests.m b/examples/default/ios/InstabugTests/InstabugSampleTests.m index bf4edbd43..70ef41025 100644 --- a/examples/default/ios/InstabugTests/InstabugSampleTests.m +++ b/examples/default/ios/InstabugTests/InstabugSampleTests.m @@ -8,11 +8,11 @@ #import #import "OCMock/OCMock.h" #import "Instabug/Instabug.h" -#import "Instabug/IBGSurvey.h" #import "InstabugReactBridge.h" #import #import "IBGConstants.h" #import "RNInstabug.h" +#import @protocol InstabugCPTestProtocol /** @@ -72,7 +72,7 @@ - (void)testInit { NSArray *invocationEvents = [NSArray arrayWithObjects:[NSNumber numberWithInteger:floatingButtonInvocationEvent], nil]; BOOL useNativeNetworkInterception = YES; IBGSDKDebugLogsLevel sdkDebugLogsLevel = IBGSDKDebugLogsLevelDebug; - + OCMStub([mock setCodePushVersion:codePushVersion]); [self.instabugBridge init:appToken invocationEvents:invocationEvents debugLogsLevel:sdkDebugLogsLevel useNativeNetworkInterception:useNativeNetworkInterception codePushVersion:codePushVersion]; @@ -84,9 +84,9 @@ - (void)testInit { - (void)testSetCodePushVersion { id mock = OCMClassMock([Instabug class]); NSString *codePushVersion = @"123"; - + [self.instabugBridge setCodePushVersion:codePushVersion]; - + OCMVerify([mock setCodePushVersion:codePushVersion]); } @@ -313,6 +313,61 @@ - (void)testSetWelcomeMessageMode { OCMVerify([mock setWelcomeMessageMode:welcomeMessageMode]); } +- (void)testNetworkLogIOS { + id mIBGNetworkLogger = OCMClassMock([IBGNetworkLogger class]); + + NSString *url = @"https://api.instabug.com"; + NSString *method = @"GET"; + NSString *requestBody = @"requestBody"; + double requestBodySize = 10; + NSString *responseBody = @"responseBody"; + double responseBodySize = 15; + double responseCode = 200; + NSDictionary *requestHeaders = @{ @"accept": @"application/json" }; + NSDictionary *responseHeaders = @{ @"cache-control": @"no-store" }; + NSString *contentType = @"application/json"; + double errorCode = 0; + NSString *errorDomain = nil; + double startTime = 1719847101199; + double duration = 150; + NSString *gqlQueryName = nil; + NSString *serverErrorMessage = nil; + + [self.instabugBridge networkLogIOS:url + method:method + requestBody:requestBody + requestBodySize:requestBodySize + responseBody:responseBody + responseBodySize:responseBodySize + responseCode:responseCode + requestHeaders:requestHeaders + responseHeaders:responseHeaders + contentType:contentType + errorDomain:errorDomain + errorCode:errorCode + startTime:startTime + duration:duration + gqlQueryName:gqlQueryName + serverErrorMessage:serverErrorMessage]; + + OCMVerify([mIBGNetworkLogger addNetworkLogWithUrl:url + method:method + requestBody:requestBody + requestBodySize:requestBodySize + responseBody:responseBody + responseBodySize:responseBodySize + responseCode:responseCode + requestHeaders:requestHeaders + responseHeaders:responseHeaders + contentType:contentType + errorDomain:errorDomain + errorCode:errorCode + startTime:startTime * 1000 + duration:duration * 1000 + gqlQueryName:gqlQueryName + serverErrorMessage:serverErrorMessage]); +} + - (void)testSetFileAttachment { id mock = OCMClassMock([Instabug class]); NSString *fileLocation = @"test"; @@ -442,4 +497,43 @@ - (void)testClearAllExperiments { OCMVerify([mock clearAllExperiments]); } +- (void)testAddFeatureFlags { + id mock = OCMClassMock([Instabug class]); + NSDictionary *featureFlagsMap = @{ @"key13" : @"value1", @"key2" : @"value2"}; + + OCMStub([mock addFeatureFlags :[OCMArg any]]); + [self.instabugBridge addFeatureFlags:featureFlagsMap]; + OCMVerify([mock addFeatureFlags: [OCMArg checkWithBlock:^(id value) { + NSArray *featureFlags = value; + NSString* firstFeatureFlagName = [featureFlags objectAtIndex:0 ].name; + NSString* firstFeatureFlagKey = [[featureFlagsMap allKeys] objectAtIndex:0] ; + if([ firstFeatureFlagKey isEqualToString: firstFeatureFlagName]){ + return YES; + } + return NO; + }]]); +} + +- (void)testRemoveFeatureFlags { + id mock = OCMClassMock([Instabug class]); + NSArray *featureFlags = @[@"exp1", @"exp2"]; + [self.instabugBridge removeFeatureFlags:featureFlags]; + OCMVerify([mock removeFeatureFlags: [OCMArg checkWithBlock:^(id value) { + NSArray *featureFlagsObJ = value; + NSString* firstFeatureFlagName = [featureFlagsObJ objectAtIndex:0 ].name; + NSString* firstFeatureFlagKey = [featureFlags firstObject] ; + if([ firstFeatureFlagKey isEqualToString: firstFeatureFlagName]){ + return YES; + } + return NO; + }]]); +} + +- (void)testRemoveAllFeatureFlags { + id mock = OCMClassMock([Instabug class]); + OCMStub([mock removeAllFeatureFlags]); + [self.instabugBridge removeAllFeatureFlags]; + OCMVerify([mock removeAllFeatureFlags]); +} + @end diff --git a/examples/default/ios/InstabugTests/RNInstabugTests.m b/examples/default/ios/InstabugTests/RNInstabugTests.m index 0df2fd843..cde248ad3 100644 --- a/examples/default/ios/InstabugTests/RNInstabugTests.m +++ b/examples/default/ios/InstabugTests/RNInstabugTests.m @@ -1,7 +1,6 @@ #import #import "OCMock/OCMock.h" #import "Instabug/Instabug.h" -#import "Instabug/IBGSurvey.h" #import #import "RNInstabug.h" #import "RNInstabug/Instabug+CP.h" diff --git a/examples/default/ios/InstabugTests/Util/IBGCrashReporting+CP.h b/examples/default/ios/InstabugTests/Util/IBGCrashReporting+CP.h new file mode 100644 index 000000000..4229dbcea --- /dev/null +++ b/examples/default/ios/InstabugTests/Util/IBGCrashReporting+CP.h @@ -0,0 +1,13 @@ +#import + + +@interface IBGCrashReporting (CP) + ++ (void)cp_reportFatalCrashWithStackTrace:(NSDictionary*)stackTrace; + ++ (void)cp_reportNonFatalCrashWithStackTrace:(NSDictionary*)stackTrace + level:(IBGNonFatalLevel)level + groupingString:(NSString *)groupingString + userAttributes:(NSDictionary *)userAttributes; +@end + diff --git a/examples/default/ios/Podfile b/examples/default/ios/Podfile index 7fa5129f9..3d0c8ceed 100644 --- a/examples/default/ios/Podfile +++ b/examples/default/ios/Podfile @@ -5,17 +5,6 @@ require_relative '../node_modules/@react-native-community/cli-platform-ios/nativ platform :ios, '13.4' prepare_react_native_project! -# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set. -# because `react-native-flipper` depends on (FlipperKit,...) that will be excluded -# -# To fix this you can also exclude `react-native-flipper` using a `react-native.config.js` -# ```js -# module.exports = { -# dependencies: { -# ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}), -# ``` -flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled - linkage = ENV['USE_FRAMEWORKS'] if linkage != nil Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green @@ -36,11 +25,6 @@ target 'InstabugExample' do # we make it explicit here to aid in the React Native upgrade process. :hermes_enabled => flags[:hermes_enabled], :fabric_enabled => flags[:fabric_enabled], - # Enables Flipper. - # - # Note that if you have use_frameworks! enabled, Flipper will not work and - # you should disable the next line. - :flipper_configuration => flipper_config, # An absolute path to your application root. :app_path => "#{Pod::Config.instance.installation_root}/.." ) diff --git a/examples/default/ios/Podfile.lock b/examples/default/ios/Podfile.lock index f89a967e5..1da6e558e 100644 --- a/examples/default/ios/Podfile.lock +++ b/examples/default/ios/Podfile.lock @@ -1,6 +1,5 @@ PODS: - boost (1.76.0) - - CocoaAsyncSocket (7.6.5) - DoubleConversion (1.1.6) - FBLazyVector (0.72.3) - FBReactNativeSpec (0.72.3): @@ -10,64 +9,6 @@ PODS: - React-Core (= 0.72.3) - React-jsi (= 0.72.3) - ReactCommon/turbomodule/core (= 0.72.3) - - Flipper (0.182.0): - - Flipper-Folly (~> 2.6) - - Flipper-Boost-iOSX (1.76.0.1.11) - - Flipper-DoubleConversion (3.2.0.1) - - Flipper-Fmt (7.1.7) - - Flipper-Folly (2.6.10): - - Flipper-Boost-iOSX - - Flipper-DoubleConversion - - Flipper-Fmt (= 7.1.7) - - Flipper-Glog - - libevent (~> 2.1.12) - - OpenSSL-Universal (= 1.1.1100) - - Flipper-Glog (0.5.0.5) - - Flipper-PeerTalk (0.0.4) - - FlipperKit (0.182.0): - - FlipperKit/Core (= 0.182.0) - - FlipperKit/Core (0.182.0): - - Flipper (~> 0.182.0) - - FlipperKit/CppBridge - - FlipperKit/FBCxxFollyDynamicConvert - - FlipperKit/FBDefines - - FlipperKit/FKPortForwarding - - SocketRocket (~> 0.6.0) - - FlipperKit/CppBridge (0.182.0): - - Flipper (~> 0.182.0) - - FlipperKit/FBCxxFollyDynamicConvert (0.182.0): - - Flipper-Folly (~> 2.6) - - FlipperKit/FBDefines (0.182.0) - - FlipperKit/FKPortForwarding (0.182.0): - - CocoaAsyncSocket (~> 7.6) - - Flipper-PeerTalk (~> 0.0.4) - - FlipperKit/FlipperKitHighlightOverlay (0.182.0) - - FlipperKit/FlipperKitLayoutHelpers (0.182.0): - - FlipperKit/Core - - FlipperKit/FlipperKitHighlightOverlay - - FlipperKit/FlipperKitLayoutTextSearchable - - FlipperKit/FlipperKitLayoutIOSDescriptors (0.182.0): - - FlipperKit/Core - - FlipperKit/FlipperKitHighlightOverlay - - FlipperKit/FlipperKitLayoutHelpers - - YogaKit (~> 1.18) - - FlipperKit/FlipperKitLayoutPlugin (0.182.0): - - FlipperKit/Core - - FlipperKit/FlipperKitHighlightOverlay - - FlipperKit/FlipperKitLayoutHelpers - - FlipperKit/FlipperKitLayoutIOSDescriptors - - FlipperKit/FlipperKitLayoutTextSearchable - - YogaKit (~> 1.18) - - FlipperKit/FlipperKitLayoutTextSearchable (0.182.0) - - FlipperKit/FlipperKitNetworkPlugin (0.182.0): - - FlipperKit/Core - - FlipperKit/FlipperKitReactPlugin (0.182.0): - - FlipperKit/Core - - FlipperKit/FlipperKitUserDefaultsPlugin (0.182.0): - - FlipperKit/Core - - FlipperKit/SKIOSNetworkPlugin (0.182.0): - - FlipperKit/Core - - FlipperKit/FlipperKitNetworkPlugin - fmt (6.2.1) - glog (0.3.5) - Google-Maps-iOS-Utils (4.2.2): @@ -97,13 +38,12 @@ PODS: - hermes-engine (0.72.3): - hermes-engine/Pre-built (= 0.72.3) - hermes-engine/Pre-built (0.72.3) - - Instabug (13.0.5) + - Instabug (13.3.0) - instabug-reactnative-ndk (0.1.0): - RCT-Folly (= 2021.07.22.00) - React-Core - libevent (2.1.12) - OCMock (3.9.3) - - OpenSSL-Universal (1.1.1100) - RCT-Folly (2021.07.22.00): - boost - DoubleConversion @@ -404,6 +344,8 @@ PODS: - React-jsinspector (0.72.3) - React-logger (0.72.3): - glog + - react-native-background-timer (2.4.1): + - React-Core - react-native-config (1.5.1): - react-native-config/App (= 1.5.1) - react-native-config/App (1.5.1): @@ -528,11 +470,13 @@ PODS: - React-jsi (= 0.72.3) - React-logger (= 0.72.3) - React-perflogger (= 0.72.3) + - RNCClipboard (1.5.1): + - React-Core - RNGestureHandler (2.13.4): - RCT-Folly (= 2021.07.22.00) - React-Core - - RNInstabug (13.0.5): - - Instabug (= 13.0.5) + - RNInstabug (13.3.0): + - Instabug (= 13.3.0) - React-Core - RNReanimated (3.5.4): - DoubleConversion @@ -572,40 +516,17 @@ PODS: - React-Core - SocketRocket (0.6.1) - Yoga (1.14.0) - - YogaKit (1.18.1): - - Yoga (~> 1.14) DEPENDENCIES: - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`) - - Flipper (= 0.182.0) - - Flipper-Boost-iOSX (= 1.76.0.1.11) - - Flipper-DoubleConversion (= 3.2.0.1) - - Flipper-Fmt (= 7.1.7) - - Flipper-Folly (= 2.6.10) - - Flipper-Glog (= 0.5.0.5) - - Flipper-PeerTalk (= 0.0.4) - - FlipperKit (= 0.182.0) - - FlipperKit/Core (= 0.182.0) - - FlipperKit/CppBridge (= 0.182.0) - - FlipperKit/FBCxxFollyDynamicConvert (= 0.182.0) - - FlipperKit/FBDefines (= 0.182.0) - - FlipperKit/FKPortForwarding (= 0.182.0) - - FlipperKit/FlipperKitHighlightOverlay (= 0.182.0) - - FlipperKit/FlipperKitLayoutPlugin (= 0.182.0) - - FlipperKit/FlipperKitLayoutTextSearchable (= 0.182.0) - - FlipperKit/FlipperKitNetworkPlugin (= 0.182.0) - - FlipperKit/FlipperKitReactPlugin (= 0.182.0) - - FlipperKit/FlipperKitUserDefaultsPlugin (= 0.182.0) - - FlipperKit/SKIOSNetworkPlugin (= 0.182.0) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) - instabug-reactnative-ndk (from `../node_modules/instabug-reactnative-ndk`) - libevent (~> 2.1.12) - OCMock - - OpenSSL-Universal (= 1.1.1100) - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) @@ -613,7 +534,6 @@ DEPENDENCIES: - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) - React-Codegen (from `build/generated/ios`) - React-Core (from `../node_modules/react-native/`) - - React-Core/DevSupport (from `../node_modules/react-native/`) - React-Core/RCTWebSocket (from `../node_modules/react-native/`) - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) @@ -623,6 +543,7 @@ DEPENDENCIES: - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) - React-logger (from `../node_modules/react-native/ReactCommon/logger`) + - react-native-background-timer (from `../node_modules/react-native-background-timer`) - react-native-config (from `../node_modules/react-native-config`) - react-native-google-maps (from `../node_modules/react-native-maps`) - react-native-maps (from `../node_modules/react-native-maps`) @@ -645,6 +566,7 @@ DEPENDENCIES: - React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`) - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - "RNCClipboard (from `../node_modules/@react-native-community/clipboard`)" - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNInstabug (from `../node_modules/instabug-reactnative`) - RNReanimated (from `../node_modules/react-native-reanimated`) @@ -655,24 +577,13 @@ DEPENDENCIES: SPEC REPOS: trunk: - - CocoaAsyncSocket - - Flipper - - Flipper-Boost-iOSX - - Flipper-DoubleConversion - - Flipper-Fmt - - Flipper-Folly - - Flipper-Glog - - Flipper-PeerTalk - - FlipperKit - fmt - Google-Maps-iOS-Utils - GoogleMaps - Instabug - libevent - OCMock - - OpenSSL-Universal - SocketRocket - - YogaKit EXTERNAL SOURCES: boost: @@ -720,6 +631,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/jsinspector" React-logger: :path: "../node_modules/react-native/ReactCommon/logger" + react-native-background-timer: + :path: "../node_modules/react-native-background-timer" react-native-config: :path: "../node_modules/react-native-config" react-native-google-maps: @@ -764,6 +677,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/react/utils" ReactCommon: :path: "../node_modules/react-native/ReactCommon" + RNCClipboard: + :path: "../node_modules/@react-native-community/clipboard" RNGestureHandler: :path: "../node_modules/react-native-gesture-handler" RNInstabug: @@ -781,28 +696,18 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 7dcd2de282d72e344012f7d6564d024930a6a440 - CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 FBLazyVector: 4cce221dd782d3ff7c4172167bba09d58af67ccb FBReactNativeSpec: c6bd9e179757b3c0ecf815864fae8032377903ef - Flipper: 6edb735e6c3e332975d1b17956bcc584eccf5818 - Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c - Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30 - Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b - Flipper-Folly: 584845625005ff068a6ebf41f857f468decd26b3 - Flipper-Glog: 70c50ce58ddaf67dc35180db05f191692570f446 - Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 - FlipperKit: 2efad7007d6745a3f95e4034d547be637f89d3f6 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b Google-Maps-iOS-Utils: f77eab4c4326d7e6a277f8e23a0232402731913a GoogleMaps: 032f676450ba0779bd8ce16840690915f84e57ac hermes-engine: 10fbd3f62405c41ea07e71973ea61e1878d07322 - Instabug: 54e81af4202faf8ed1e26e47f8816d6c42245d1e + Instabug: 4f26295103a330ec0236918359eef7ccaa74e2fa instabug-reactnative-ndk: 960119a69380cf4cbe47ccd007c453f757927d17 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 OCMock: 300b1b1b9155cb6378660b981c2557448830bdc6 - OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 RCTRequired: a2faf4bad4e438ca37b2040cb8f7799baa065c18 RCTTypeSafety: cb09f3e4747b6d18331a15eb05271de7441ca0b3 @@ -818,6 +723,7 @@ SPEC CHECKSUMS: React-jsiexecutor: 59d1eb03af7d30b7d66589c410f13151271e8006 React-jsinspector: b511447170f561157547bc0bef3f169663860be7 React-logger: c5b527272d5f22eaa09bb3c3a690fee8f237ae95 + react-native-background-timer: 17ea5e06803401a379ebf1f20505b793ac44d0fe react-native-config: 86038147314e2e6d10ea9972022aa171e6b1d4d8 react-native-google-maps: 1bcc1f9f13f798fcf230db7fe476f3566d0bc0a3 react-native-maps: 72a8a903f8a1b53e2c777ba79102078ab502e0bf @@ -840,16 +746,16 @@ SPEC CHECKSUMS: React-runtimescheduler: 837c1bebd2f84572db17698cd702ceaf585b0d9a React-utils: bcb57da67eec2711f8b353f6e3d33bd8e4b2efa3 ReactCommon: 3ccb8fb14e6b3277e38c73b0ff5e4a1b8db017a9 + RNCClipboard: 41d8d918092ae8e676f18adada19104fa3e68495 RNGestureHandler: 6e46dde1f87e5f018a54fe5d40cd0e0b942b49ee - RNInstabug: 5284a7dc148edd3184a6510e9bae0f9f5ef8b254 + RNInstabug: a4ac0bd09123f6be7d58be541dc220acbaff8dc3 RNReanimated: ab2e96c6d5591c3dfbb38a464f54c8d17fb34a87 RNScreens: b21dc57dfa2b710c30ec600786a3fc223b1b92e7 RNSVG: 80584470ff1ffc7994923ea135a3e5ad825546b9 RNVectorIcons: 8b5bb0fa61d54cd2020af4f24a51841ce365c7e9 SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 Yoga: 8796b55dba14d7004f980b54bcc9833ee45b28ce - YogaKit: f782866e155069a2cca2517aafea43200b01fd5a -PODFILE CHECKSUM: ad82a989387dcdb1ce80667f16b998a2644cf0b2 +PODFILE CHECKSUM: 281036e04bd4b9e7c2cc03a503b3245d3f1dd0dd COCOAPODS: 1.12.0 diff --git a/examples/default/package.json b/examples/default/package.json index 5c6b173c1..a618c2811 100644 --- a/examples/default/package.json +++ b/examples/default/package.json @@ -10,14 +10,20 @@ "postinstall": "patch-package" }, "dependencies": { + "@react-native-community/clipboard": "^1.5.1", "@react-native-community/slider": "^4.4.3", "@react-navigation/bottom-tabs": "^6.5.7", "@react-navigation/native": "^6.1.6", "@react-navigation/native-stack": "^6.9.12", + "graphql": "^16.8.1", + "axios": "^1.7.4", + "graphql-request": "^6.1.0", "instabug-reactnative": "link:../..", + "instabug-reactnative-ndk": "github:https://github.com/Instabug/Instabug-React-Native-NDK", "native-base": "^3.4.28", "react": "18.2.0", "react-native": "0.72.3", + "react-native-background-timer": "^2.4.1", "react-native-config": "^1.5.1", "react-native-gesture-handler": "^2.13.4", "react-native-maps": "^1.10.3", @@ -26,7 +32,7 @@ "react-native-screens": "^3.20.0", "react-native-svg": "^13.9.0", "react-native-vector-icons": "^10.0.0", - "instabug-reactnative-ndk": "github:https://github.com/Instabug/Instabug-React-Native-NDK" + "react-query": "^3.39.3" }, "devDependencies": { "@babel/core": "^7.20.0", @@ -35,6 +41,7 @@ "@react-native/metro-config": "^0.73.1", "@types/jest": "^29.2.1", "@types/react": "^18.0.24", + "@types/react-native-background-timer": "^2.0.2", "@types/react-native-vector-icons": "^6.4.13", "@types/react-test-renderer": "^18.0.0", "babel-jest": "^29.2.1", diff --git a/examples/default/src/App.tsx b/examples/default/src/App.tsx index 4bc144705..33a3c34f9 100644 --- a/examples/default/src/App.tsx +++ b/examples/default/src/App.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react'; import { StyleSheet } from 'react-native'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; -import { NavigationContainer } from '@react-navigation/native'; +import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native'; import Instabug, { CrashReporting, InvocationEvent, @@ -15,7 +15,12 @@ import { RootTabNavigator } from './navigation/RootTab'; import { nativeBaseTheme } from './theme/nativeBaseTheme'; import { navigationTheme } from './theme/navigationTheme'; +import { QueryClient, QueryClientProvider } from 'react-query'; + +const queryClient = new QueryClient(); + export const App: React.FC = () => { + const navigationRef = useNavigationContainerRef(); useEffect(() => { Instabug.init({ token: 'deb1910a7342814af4e4c9210c786f35', @@ -29,12 +34,20 @@ export const App: React.FC = () => { }); }, []); + useEffect(() => { + const unregisterListener = Instabug.setNavigationListener(navigationRef); + + return unregisterListener; + }, [navigationRef]); + return ( - - - + + + + + ); diff --git a/examples/default/src/components/ClipboardTextInput.tsx b/examples/default/src/components/ClipboardTextInput.tsx new file mode 100644 index 000000000..05d9c35ec --- /dev/null +++ b/examples/default/src/components/ClipboardTextInput.tsx @@ -0,0 +1,36 @@ +import React, { PropsWithChildren } from 'react'; +import { StyleProp, StyleSheet, TextStyle } from 'react-native'; +import PasteFromClipboardButton from './PasteFromClipboardButton'; +import { HStack } from 'native-base'; +import { InputField } from './InputField'; + +interface ClipboardTextInputProps extends PropsWithChildren { + placeholder?: string; + onChangeText?: (text: string) => void; + value?: string; + selectTextOnFocus?: boolean; + style?: StyleProp; +} + +export const ClipboardTextInput: React.FC = ({ + onChangeText, + style, + ...restProps +}) => { + return ( + + + + + ); + + function handleClipboardPress(text: string) { + onChangeText?.call(undefined, text); + } +}; + +export const styles = StyleSheet.create({ + inputField: { + flex: 1, + }, +}); diff --git a/examples/default/src/components/CustomButton.tsx b/examples/default/src/components/CustomButton.tsx new file mode 100644 index 000000000..07bcf9798 --- /dev/null +++ b/examples/default/src/components/CustomButton.tsx @@ -0,0 +1,31 @@ +import React, { PropsWithChildren } from 'react'; +import type { StyleProp, TextStyle } from 'react-native'; +import { Button } from 'native-base'; +import { CustomText } from './CustomText'; +import { StyleSheet } from 'react-native'; + +interface CustomButtonProps extends PropsWithChildren { + title?: string; + onPress?: () => void; + style?: StyleProp; +} + +export const CustomButton: React.FC = ({ + style, + title, + children, + ...restProps +}) => { + return ( + + ); +}; + +export const styles = StyleSheet.create({ + text: { + color: '#fff', + }, +}); diff --git a/examples/default/src/components/CustomImage.tsx b/examples/default/src/components/CustomImage.tsx new file mode 100644 index 000000000..5c5d051eb --- /dev/null +++ b/examples/default/src/components/CustomImage.tsx @@ -0,0 +1,17 @@ +import React, { PropsWithChildren } from 'react'; +import { StyleProp, StyleSheet, TextStyle, Text } from 'react-native'; + +interface CustomTextProps extends PropsWithChildren { + style?: StyleProp; +} + +export const CustomImage: React.FC = ({ style, children }) => { + return {children}; +}; + +export const styles = StyleSheet.create({ + text: { + fontSize: 16, + lineHeight: 24, + }, +}); diff --git a/examples/default/src/components/CustomText.tsx b/examples/default/src/components/CustomText.tsx new file mode 100644 index 000000000..5e3622128 --- /dev/null +++ b/examples/default/src/components/CustomText.tsx @@ -0,0 +1,17 @@ +import React, { PropsWithChildren } from 'react'; +import { StyleProp, StyleSheet, TextStyle, Text } from 'react-native'; + +interface CustomTextProps extends PropsWithChildren { + style?: StyleProp; +} + +export const CustomText: React.FC = ({ style, children }) => { + return {children}; +}; + +export const styles = StyleSheet.create({ + text: { + fontSize: 16, + lineHeight: 24, + }, +}); diff --git a/examples/default/src/components/InputField.tsx b/examples/default/src/components/InputField.tsx index 1a5700c27..feef8fd7b 100644 --- a/examples/default/src/components/InputField.tsx +++ b/examples/default/src/components/InputField.tsx @@ -1,13 +1,22 @@ import React, { forwardRef } from 'react'; -import { KeyboardTypeOptions, StyleSheet, TextInput, View } from 'react-native'; +import { + KeyboardTypeOptions, + StyleProp, + StyleSheet, + TextInput, + ViewStyle, + View, +} from 'react-native'; import { Text } from 'native-base'; interface InputFieldProps { placeholder?: string; value?: string; + style?: StyleProp; onChangeText?: (text: string) => void; keyboardType?: KeyboardTypeOptions; + selectTextOnFocus?: boolean | undefined; errorText?: string; maxLength?: number; accessibilityLabel?: string; @@ -19,6 +28,7 @@ export const InputField = forwardRef( { placeholder, value, + style, onChangeText, accessibilityLabel, maxLength, @@ -33,7 +43,7 @@ export const InputField = forwardRef( void; + style?: StyleProp; +} + +const PasteFromClipboardButton: React.FC = ({ onPress, style }) => { + const handlePress = async () => { + const text = await Clipboard.getString(); + onPress?.call(undefined, text); // Using call to pass the text to the callback + }; + + return ( + + + + ); +}; + +export default PasteFromClipboardButton; diff --git a/examples/default/src/navigation/HomeStack.tsx b/examples/default/src/navigation/HomeStack.tsx index 184e36f03..b4a8ca3e6 100644 --- a/examples/default/src/navigation/HomeStack.tsx +++ b/examples/default/src/navigation/HomeStack.tsx @@ -21,6 +21,10 @@ import { } from '../screens/user-steps/BackAndForthScreen'; import { GoogleMapsScreen } from '../screens/user-steps/GoogleMapsScreen'; import { LargeImageListScreen } from '../screens/user-steps/LargeImageListScreen'; +import { APMScreen } from '../screens/apm/APMScreen'; +import { TracesScreen } from '../screens/apm/TracesScreen'; +import { NetworkScreen } from '../screens/apm/NetworkScreen'; +import { FlowsScreen } from '../screens/apm/FlowsScreen'; import { SessionReplayScreen } from '../screens/SessionReplayScreen'; export type HomeStackParamList = { @@ -41,6 +45,12 @@ export type HomeStackParamList = { LargeImageList: undefined; SessionReplay: undefined; BackAndForthScreen: BackAndForthScreenProp; + + // APM // + APM: undefined; + NetworkTraces: undefined; + ExecutionTraces: undefined; + AppFlows: undefined; }; const HomeStack = createNativeStackNavigator(); @@ -65,7 +75,6 @@ export const HomeStackNavigator: React.FC = () => { options={{ title: 'Feature Requests' }} /> - { options={{ title: 'Large Image List' }} /> + + + + ); }; diff --git a/examples/default/src/screens/CrashReportingScreen.tsx b/examples/default/src/screens/CrashReportingScreen.tsx index 24eae5113..c5a53567f 100644 --- a/examples/default/src/screens/CrashReportingScreen.tsx +++ b/examples/default/src/screens/CrashReportingScreen.tsx @@ -1,13 +1,29 @@ -import React from 'react'; -import { Alert, Platform, ScrollView, Text, View } from 'react-native'; +import React, { useState } from 'react'; +import { Alert, Platform, ScrollView, StyleSheet, Text, View } from 'react-native'; -import { CrashReporting } from 'instabug-reactnative'; +import { CrashReporting, NonFatalErrorLevel } from 'instabug-reactnative'; import { ListTile } from '../components/ListTile'; import { Screen } from '../components/Screen'; import { Section } from '../components/Section'; import { PlatformListTile } from '../components/PlatformListTile'; import { NativeExampleCrashReporting } from '../native/NativeCrashReporting'; +import { VerticalListTile } from '../components/VerticalListTile'; +import { Button, VStack } from 'native-base'; +import { InputField } from '../components/InputField'; +import { Select } from '../components/Select'; + +const styles = StyleSheet.create({ + inputWrapper: { + padding: 4, + flex: 1, + }, + + formContainer: { + flexDirection: 'row', + alignItems: 'stretch', + }, +}); export const CrashReportingScreen: React.FC = () => { function throwHandledException(error: Error) { @@ -19,7 +35,7 @@ export const CrashReportingScreen: React.FC = () => { throw error; } catch (err) { if (err instanceof Error) { - CrashReporting.reportError(err).then(() => + CrashReporting.reportError(err, { level: NonFatalErrorLevel.critical }).then(() => Alert.alert(`Crash report for ${error.name} is Sent!`), ); } @@ -46,6 +62,41 @@ export const CrashReportingScreen: React.FC = () => { } } + const [userAttributeKey, setUserAttributeKey] = useState(''); + const [userAttributeValue, setUserAttributeValue] = useState(''); + const [crashNameValue, setCrashNameValue] = useState(''); + const [crashFingerprint, setCrashFingerprint] = useState(''); + const [crashLevelValue, setCrashLevelValue] = useState( + NonFatalErrorLevel.error, + ); + + function sendCrash() { + try { + const error = new Error(crashNameValue); + + throw error; + } catch (err) { + if (err instanceof Error) { + const attrMap: { [k: string]: string } = {}; + attrMap[userAttributeKey] = userAttributeValue; + + const userAttributes: Record = {}; + if (userAttributeKey && userAttributeValue) { + userAttributes[userAttributeKey] = userAttributeValue; + } + const fingerprint = crashFingerprint.length === 0 ? undefined : crashFingerprint; + + CrashReporting.reportError(err, { + userAttributes: userAttributes, + fingerprint: fingerprint, + level: crashLevelValue, + }).then(() => { + Alert.alert(`Crash report for ${crashNameValue} is Sent!`); + }); + } + } + } + return ( @@ -74,6 +125,71 @@ export const CrashReportingScreen: React.FC = () => { title="Throw Handled Native Exception" onPress={() => NativeExampleCrashReporting.sendNativeNonFatal()} /> + + + + setCrashNameValue(key)} + value={crashNameValue} + /> + + + + setUserAttributeKey(key)} + value={userAttributeKey} + /> + + + setUserAttributeValue(value)} + value={userAttributeValue} + /> + + + +