Skip to content

Conversation

runningcode
Copy link
Contributor

@runningcode runningcode commented Sep 17, 2025

Summary

Implements the checkForUpdateBlocking method in DistributionIntegration to check for app updates via Sentry's distribution API.

Why not reuse existing HttpConnection?

The existing HttpConnection class is designed specifically for Sentry event transport and is not suitable for distribution API calls:

  • Hardcoded for POST requests (we need GET)
  • Expects Sentry envelopes with gzip encoding (we need simple JSON)
  • Only considers status 200 successful (REST APIs use 200-299 range)
  • Includes Sentry-specific rate limiting logic

Changes

  • DistributionHttpClient: New HTTP client for distribution API requests

    • Supports GET requests with query parameters (main_binary_identifier, app_id, platform, version)
    • Uses SentryOptions.DistributionOptions for configuration (orgSlug, projectSlug, orgAuthToken)
  • UpdateResponseParser: JSON response parser for API responses

#skip-changelog

🤖 Generated with Claude Code

Copy link
Contributor

github-actions bot commented Sep 17, 2025

Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against 94d8b93

@runningcode runningcode force-pushed the no/implement-distribution-check-for-updates branch from 57a565d to 3e93cc4 Compare September 17, 2025 13:22
Copy link
Contributor

github-actions bot commented Sep 17, 2025

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 363.09 ms 427.88 ms 64.79 ms
Size 1.58 MiB 2.10 MiB 533.39 KiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
c8125f3 397.65 ms 485.14 ms 87.49 ms
ee747ae 386.94 ms 431.43 ms 44.49 ms
23d6b12 354.10 ms 408.38 ms 54.28 ms
d217708 409.83 ms 474.72 ms 64.89 ms
d217708 411.22 ms 430.86 ms 19.63 ms
ee747ae 400.46 ms 423.61 ms 23.15 ms
17a0955 372.53 ms 446.70 ms 74.17 ms
3d205d0 352.15 ms 432.53 ms 80.38 ms
1df7eb6 397.04 ms 429.64 ms 32.60 ms
b750b96 408.98 ms 480.32 ms 71.34 ms

App size

Revision Plain With Sentry Diff
c8125f3 1.58 MiB 2.10 MiB 532.32 KiB
ee747ae 1.58 MiB 2.10 MiB 530.95 KiB
23d6b12 1.58 MiB 2.10 MiB 532.31 KiB
d217708 1.58 MiB 2.10 MiB 532.97 KiB
d217708 1.58 MiB 2.10 MiB 532.97 KiB
ee747ae 1.58 MiB 2.10 MiB 530.95 KiB
17a0955 1.58 MiB 2.10 MiB 533.20 KiB
3d205d0 1.58 MiB 2.10 MiB 532.97 KiB
1df7eb6 1.58 MiB 2.10 MiB 532.97 KiB
b750b96 1.58 MiB 2.10 MiB 533.19 KiB

Previous results on branch: no/implement-distribution-check-for-updates

Startup times

Revision Plain With Sentry Diff
6666e36 374.88 ms 444.90 ms 70.02 ms
7b367ae 349.24 ms 457.92 ms 108.68 ms
c85da5d 417.36 ms 489.36 ms 72.00 ms

App size

Revision Plain With Sentry Diff
6666e36 1.58 MiB 2.10 MiB 532.97 KiB
7b367ae 1.58 MiB 2.10 MiB 533.39 KiB
c85da5d 1.58 MiB 2.10 MiB 532.97 KiB

import javax.net.ssl.HttpsURLConnection

/** HTTP client for making requests to Sentry's distribution API. */
internal class DistributionHttpClient(private val options: SentryOptions) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had claude generate an HttpClient for me. It works. I'm just not sure if it is a good idea to develop another client.

The other alternative is to expand the existing HttpConnection to support our use case OR shadow oktthp in to our library. Open to thoughts!

import org.junit.Ignore
import org.junit.Test

class DistributionHttpClientTest {
Copy link
Contributor Author

@runningcode runningcode Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a test class I used to test the integration. In a future, SAGP will inject the authToken and other things so we won't need to specify this manually. I'll also integrate distribution in to the sample app.

@runningcode runningcode requested a review from chromy September 17, 2025 13:37
@runningcode runningcode changed the title feat(android-distribution): implement checkForUpdateBlocking functionality feat(android-distribution): add httpclient for checking for build distribution updates Sep 18, 2025
Copy link
Contributor

@chromy chromy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

UpdateStatus.UpdateError(e.message ?: "Configuration error")
} catch (e: Exception) {
sentryOptions.logger.log(SentryLevel.ERROR, e, "Failed to check for updates")
UpdateStatus.UpdateError("Network error: ${e.message}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be nice to be able to distinguish between 'not connected' and 'network error' in the return type if that is possible and easy.

Copy link
Contributor Author

@runningcode runningcode Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UnknownHostException would mean there is possibly no network and SocketTimeoutException could mean a bad network. I added those to distinguish here.

runningcode and others added 4 commits September 22, 2025 16:20
…ality

Implements the checkForUpdateBlocking method in DistributionIntegration to check for app updates via Sentry's distribution API.

## Why not reuse existing HttpConnection?

The existing `HttpConnection` class is designed specifically for Sentry event transport and is not suitable for distribution API calls:
- Hardcoded for POST requests (we need GET)
- Expects Sentry envelopes with gzip encoding (we need simple JSON)
- Only considers status 200 successful (REST APIs use 200-299 range)
- Includes Sentry-specific rate limiting logic

## Changes

- **DistributionHttpClient**: New HTTP client for distribution API requests
  - Supports GET requests with query parameters (main_binary_identifier, app_id, platform, version)
  - Uses SentryOptions.DistributionOptions for configuration (orgSlug, projectSlug, orgAuthToken)
  - Handles SSL configuration, timeouts, and proper error handling

- **UpdateResponseParser**: JSON response parser for API responses
  - Parses API responses into UpdateStatus objects (UpToDate, NewRelease, UpdateError)
  - Handles various HTTP status codes with appropriate error messages
  - Validates required fields in update information

- **DistributionIntegration**: Updated to use new classes
  - Automatically extracts app information (package name, version) from Android context
  - Clean separation of concerns with HTTP client and response parser
  - Comprehensive error handling and logging

- **Tests**: Added unit test for DistributionHttpClient with real API integration

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Remove unnecessary Claude-style comments from DistributionHttpClient
- Replace manual URL building with Android Uri.Builder for safer parameter encoding
- Add comprehensive tests for UpdateResponseParser with 11 test cases
- Improve error handling to distinguish between network connection vs server issues
- Add clarifying comments about which exceptions indicate network connectivity problems
- Fix null value handling in JSON parsing to properly validate "null" strings
- Remove unclear comment about package name usage

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Replace custom fallback "sentry-android-distribution" with error throw
when sentryClientName is null, following the pattern used throughout
the codebase where sentryClientName is expected to always be set.

Addresses PR review feedback about reusing consistent user agent.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Change "check connection speed" to "check network connection" to be
more general and align with the goal of distinguishing network
connectivity issues.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
@runningcode runningcode force-pushed the no/implement-distribution-check-for-updates branch from 6465b7d to 061d6b5 Compare September 22, 2025 14:20
@runningcode runningcode requested a review from chromy September 22, 2025 14:26
Copy link
Contributor

@chromy chromy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm % some small comments

runningcode and others added 2 commits September 26, 2025 16:17
- Add UpdateStatus.NoNetwork subclass for network-specific errors
- Update DistributionIntegration to use NoNetwork for UnknownHostException and SocketTimeoutException
- Improve UpdateResponseParser error messages to specify which required fields are missing
- Add comprehensive tests for specific missing field error messages

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Update UpdateCheckParams constructor to use separate versionCode and versionName parameters
- Replace Android Uri with string building for better compatibility
- Remove unused Android Uri import
- Update URL construction to use build_number and build_version query parameters

This fixes the CI compilation errors where the old constructor expected a single 'version' parameter.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
cursor[bot]

This comment was marked as outdated.

@runningcode runningcode enabled auto-merge (squash) September 26, 2025 15:32
- Extract versionCode properly from PackageInfo using longVersionCode for API 28+
- Fall back to deprecated versionCode for older Android versions
- Add missing mainBinaryIdentifier parameter to UpdateCheckParams constructor
- Use proper API-level checks to avoid deprecation warnings

This resolves the compilation error where 'version' was undefined.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
append("&platform=${params.platform}")
append("&build_number=${params.versionCode}")
append("&build_version=${params.versionName}")
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: URL Encoding and Null Checks Missing

The checkForUpdates method builds the URL query string without encoding parameters like mainBinaryIdentifier, appId, platform, versionCode, and versionName. This can result in malformed URLs and failed requests if these values contain special characters. Separately, orgSlug, projectSlug, authToken, and baseUrl are used without null checks, potentially causing NullPointerExceptions.

Fix in Cursor Fix in Web

packageInfo.longVersionCode.toInt()
} else {
@Suppress("DEPRECATION") packageInfo.versionCode
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Android API 28+ Version Code Truncation Bug

On Android API 28+, packageInfo.longVersionCode is converted to an Int. This can silently truncate version codes exceeding Int.MAX_VALUE, leading to incorrect version information being sent to the distribution API and potentially faulty update checks.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants