forked from ChuckerTeam/chucker
-
Notifications
You must be signed in to change notification settings - Fork 1
Root and Granular ClaudeMD File #113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mohitguptameesho
wants to merge
1
commit into
develop
Choose a base branch
from
root-and-granular-claudemd-file
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| # Chucker - Android HTTP Inspector | ||
|
|
||
| Chucker is an in-app HTTP inspector for Android. It intercepts OkHttp traffic, stores it in a Room database, and provides a built-in UI to browse requests/responses. It ships as two variants: `library` (full, for debug) and `library-no-op` (empty stubs, for release). | ||
|
|
||
| ## Modules | ||
|
|
||
| | Module | What it is | When to touch it | | ||
| |--------|-----------|-----------------| | ||
| | `library` | Full interceptor + UI + database | Adding/changing HTTP inspection features | | ||
| | `library-no-op` | Empty stubs matching library's public API | **Must update whenever library's public API changes** | | ||
| | `sample` | Demo app exercising all features | Testing changes, adding demo for new features | | ||
|
|
||
| ## Quick Start | ||
|
|
||
| ```bash | ||
| # Build everything | ||
| ./gradlew build | ||
|
|
||
| # Run tests (library only has tests) | ||
| ./gradlew :library:test | ||
|
|
||
| # Run all quality checks (do this before pushing) | ||
| ./gradlew lint ktlintCheck detekt apiCheck | ||
|
|
||
| # Auto-fix formatting | ||
| ./gradlew ktlintFormat | ||
|
|
||
| # Install sample app to test changes | ||
| ./gradlew :sample:installDebug | ||
|
|
||
| # Install git hooks (trufflehog + cac-validate + yaakhook) | ||
| ./gradlew installGitHook | ||
| ``` | ||
|
|
||
| ## How to Add a New Feature | ||
|
|
||
| ### Adding a new interceptor option (e.g., new config flag) | ||
|
|
||
| 1. **Add to `ChuckerInterceptor.Builder`** in `library/src/main/kotlin/.../api/ChuckerInterceptor.kt` | ||
| - Add an `internal var` field on the Builder | ||
| - Add a `public fun` builder method that sets it and returns `this` | ||
| - Use it in the private constructor or pass it to processors | ||
|
|
||
| 2. **Mirror in no-op** in `library-no-op/src/main/kotlin/.../api/ChuckerInterceptor.kt` | ||
| - Add the same builder method signature, but make it a no-op (just return `this`) | ||
|
|
||
| 3. **Update binary API files** β run `./gradlew apiDump` to regenerate `library/api/library.api` and `library-no-op/api/library-no-op.api` | ||
|
|
||
| 4. **Add tests** in `library/src/test/kotlin/.../api/ChuckerInterceptorTest.kt` | ||
|
|
||
| 5. **Demo it** in `sample/.../OkHttpUtils.kt` where the interceptor is built | ||
|
|
||
| ### Adding a new data field to HTTP transactions | ||
|
|
||
| 1. **Add field** to `HttpTransaction` entity in `library/.../internal/data/entity/HttpTransaction.kt` | ||
| - Must have `@ColumnInfo(name = "fieldName")` annotation | ||
| - **Increment database version** in `ChuckerDatabase.kt` (currently version 7) | ||
| - Data will be wiped on upgrade (destructive migration is enabled β this is fine for a debug lib) | ||
|
|
||
| 2. **Populate it** in `RequestProcessor` or `ResponseProcessor` in `library/.../internal/support/` | ||
|
|
||
| 3. **Display it** in the relevant UI fragment (`TransactionOverviewFragment`, `TransactionPayloadFragment`) | ||
|
|
||
| 4. **ProGuard**: `HttpTransaction` is already kept in `proguard-rules.pro`, so new fields are safe | ||
|
|
||
| ### Adding a new UI screen | ||
|
|
||
| 1. Create Activity extending `BaseChuckerActivity` in `library/.../internal/ui/` | ||
| 2. Create ViewModel extending `ViewModel` with LiveData from the repository | ||
| 3. Use ViewBinding (enabled in build config) | ||
| 4. Add to `AndroidManifest.xml` β use `android:exported="false"` for internal screens | ||
| 5. All resource names must start with `chucker_` prefix (enforced by Gradle) | ||
|
|
||
| ### Adding a new public API class | ||
|
|
||
| 1. Create in `library/.../api/` package with `public` visibility | ||
| 2. Use Builder pattern if it needs configuration | ||
| 3. Create matching no-op stub in `library-no-op/.../api/` | ||
| 4. Run `./gradlew apiDump` to update `.api` tracking files | ||
| 5. Add tests | ||
|
|
||
| ## Code Conventions | ||
|
|
||
| ### Visibility (STRICT β compiler enforced) | ||
| ```kotlin | ||
| // Every public member MUST have explicit visibility modifier | ||
| public class MyClass { // explicit public required | ||
| public fun doThing() { } // explicit public required | ||
| internal fun helper() { } // internal for library-internal use | ||
| private val cache = mutableMapOf<String, String>() | ||
| } | ||
| ``` | ||
| The `-Xexplicit-api=strict` flag means omitting `public`/`internal`/`private` is a **compile error**. | ||
|
|
||
| ### Patterns used in this codebase | ||
| - **Builder pattern** for public configurable classes (ChuckerInterceptor, ChuckerCollector) | ||
| - **Repository pattern** for data access (HttpTransactionRepository β Room DAO) | ||
| - **MVVM** for UI (ViewModel + LiveData + ViewBinding) | ||
| - **Processor pattern** for request/response handling (RequestProcessor, ResponseProcessor) | ||
| - **Null Object pattern** for no-op variant (same API, empty bodies) | ||
|
|
||
| ### Naming | ||
| - Resources: `chucker_` prefix (e.g., `chucker_ic_launcher`, `chucker_main_activity`) | ||
| - Test methods: backtick sentences (e.g., `` `image response body is available to Chucker` ``) | ||
| - Constants: `private companion object { private const val MAX_CONTENT_LENGTH = 250_000L }` | ||
|
|
||
| ### Testing | ||
| - JUnit 5 (Jupiter) β use `@Test`, `@ParameterizedTest`, `@ExtendWith` | ||
| - MockK for mocking β `mockk<ChuckerCollector>()` | ||
| - Truth for assertions β `assertThat(result).isEqualTo(expected)` | ||
| - MockWebServer for HTTP β set up server, enqueue responses, assert intercepted data | ||
| - Test utilities in `library/src/test/.../util/` (TestTransactionFactory, ClientFactory, etc.) | ||
|
|
||
| ## Things That Will Break Your PR | ||
|
|
||
| | Check | Command | Common failure | | ||
| |-------|---------|---------------| | ||
| | **Detekt** | `./gradlew detekt` | Method complexity >16, condition complexity >5, >5 return statements | | ||
| | **KtLint** | `./gradlew ktlintCheck` | Formatting issues (fix with `./gradlew ktlintFormat`) | | ||
| | **Lint** | `./gradlew lint` | Warnings treated as errors (except RtlEnabled, GradleDependency) | | ||
| | **API Check** | `./gradlew apiCheck` | Public API changed without updating `.api` files (fix with `./gradlew apiDump`) | | ||
| | **Tests** | `./gradlew :library:test` | Test failures | | ||
| | **Pre-commit hooks** | Auto on commit | TruffleHog finds secrets, CAC validation fails | | ||
|
|
||
| ## Critical Gotchas | ||
|
|
||
| 1. **Two libraries must stay in sync** β Any public API change in `library` must be mirrored in `library-no-op`. The no-op has empty implementations but identical method signatures. If they diverge, apps using the debug/release split pattern will fail to compile. | ||
|
|
||
| 2. **Database is destructive** β Room uses `fallbackToDestructiveMigration()`. Schema changes wipe all data. No migration files exist. This is intentional for a debug tool. | ||
|
|
||
| 3. **ProGuard keeps only HttpTransaction** β Only `HttpTransaction` is in `proguard-rules.pro`. If you add new entities used by Room or Gson, add them too. | ||
|
|
||
| 4. **`maxIssues: 1` in detekt** β Even 2 detekt violations will fail the build. Fix issues, don't suppress. | ||
|
|
||
| 5. **Version comes from git** β At build time, version is the current git tag (if tagged) or `branchname-SNAPSHOT`. Shallow clones may not detect tags correctly. | ||
|
|
||
| 6. **Resource prefix is enforced** β All layout, string, drawable resources must start with `chucker_`. The build will fail otherwise. | ||
|
|
||
| ## CI/CD Pipeline | ||
|
|
||
| | Workflow | Trigger | What it does | | ||
| |----------|---------|-------------| | ||
| | `pre-merge.yaml` | PRs + push to develop | Tests, lint, detekt, ktlint, apiCheck | | ||
| | `publish-snapshot.yaml` | Push to develop | Publishes `SNAPSHOT` to Sonatype | | ||
| | `publish-release.yaml` | Push git tag | Publishes release to Sonatype staging | | ||
| | `close-and-release-repository.yaml` | Manual dispatch | Promotes staging to Maven Central | | ||
| | `gradle-wrapper-validation.yml` | PRs + push to develop | Validates Gradle wrapper integrity | | ||
|
|
||
| ### How to publish a release | ||
| 1. Update `VERSION_NAME` in `gradle.properties` (e.g., `4.0.0`) | ||
| 2. Commit & tag: `git tag 4.0.0 && git push origin 4.0.0` | ||
| 3. `publish-release.yaml` auto-publishes to Sonatype staging | ||
| 4. Manually trigger `close-and-release-repository.yaml` to push to Maven Central | ||
| 5. Bump back to next snapshot: `VERSION_NAME=4.1.0-SNAPSHOT` | ||
|
|
||
| ### Publishing to JFrog Artifactory (Meesho internal) | ||
| ```bash | ||
| ./gradlew artifactoryPublish | ||
| ``` | ||
| Publishes as `com.meesho.android.chucker:library` / `com.meesho.android.chucker:library-no-op`. | ||
|
|
||
| ## Tech Stack | ||
|
|
||
| | | | | ||
| |---|---| | ||
| | **Language** | Kotlin 1.9.23, Java 17 target | | ||
| | **SDK** | minSdk 21, targetSdk 35, compileSdk 35 | | ||
| | **AGP** | 8.9.1 | | ||
| | **HTTP** | OkHttp 4.9.0 | | ||
| | **Database** | Room 2.6.1 | | ||
| | **Serialization** | Gson 2.9.0 | | ||
| | **Async** | Kotlin Coroutines 1.7.3 + LiveData | | ||
| | **UI** | AppCompat + Material 1.2.1 + ViewBinding | | ||
| | **Testing** | JUnit 5, MockK 1.10.2, Robolectric 4.4, Truth 1.1 | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| # library-no-op - Release Build Stubs | ||
|
|
||
| Zero-overhead stubs for production/release builds. Same public API as `library`, but every method is empty or passes through. Apps use this pattern: | ||
|
|
||
| ```gradle | ||
| debugImplementation project(':library') // Full Chucker in debug | ||
| releaseImplementation project(':library-no-op') // Empty stubs in release | ||
| ``` | ||
|
|
||
| ## What's Here | ||
|
|
||
| Five files in `src/main/kotlin/.../api/`, each mirroring the main library's public API: | ||
|
|
||
| | Class | What the no-op does | | ||
| |-------|-------------------| | ||
| | `ChuckerInterceptor` | `intercept()` just calls `chain.proceed(request)` β passthrough. Builder methods return `this`. | | ||
| | `Chucker` | `isOp = false`. `getLaunchIntent()` returns empty `Intent()`. | | ||
| | `ChuckerCollector` | Constructor accepts same params, stores nothing. | | ||
| | `RetentionManager` | `doMaintenance()` is empty. | | ||
| | `BodyDecoder` | Interface only (same as main library). | | ||
|
|
||
| **Dependencies:** Only OkHttp + Kotlin stdlib. No Room, no AndroidX, no Material, no Coroutines. | ||
|
|
||
| ## When You Need to Touch This | ||
|
|
||
| **Every time you change the public API in `library`:** | ||
|
|
||
| 1. Add/remove/modify the same method signature here | ||
| 2. Implementation should be empty (return `this`, return empty value, or do nothing) | ||
| 3. Run `./gradlew apiDump` to update `api/library-no-op.api` | ||
| 4. Run `./gradlew apiCheck` to verify compatibility | ||
|
|
||
| ### Example: Adding a new Builder method | ||
|
|
||
| In `library/`: | ||
| ```kotlin | ||
| public fun myNewOption(value: Boolean): Builder = apply { | ||
| this.myNewOption = value | ||
| } | ||
| ``` | ||
|
|
||
| In `library-no-op/`: | ||
| ```kotlin | ||
| @Suppress("UnusedPrivateMember") | ||
| public fun myNewOption(value: Boolean): Builder = this | ||
| ``` | ||
|
|
||
| ## Gotchas | ||
|
|
||
| - **API files may show type erasure differences** β the no-op `.api` file may show `Object` where the main library shows specific types. This is a known quirk of the binary compatibility validator with the no-op's simplified generics. | ||
| - **Don't add dependencies** β the whole point is zero overhead. No Room, no UI, no nothing. | ||
| - **Same namespace** β both modules use `com.chuckerteam.chucker` package. They're mutually exclusive at build time (debug vs release). | ||
| - **No tests** β stubs are trivial. If a stub is complex enough to need tests, it's doing too much. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| # library - Chucker Core Library | ||
|
|
||
| The main module. Contains the OkHttp interceptor, Room database, and Android UI for HTTP inspection. | ||
|
|
||
| ## Architecture | ||
|
|
||
| ``` | ||
| api/ # Public API β what app developers use | ||
| ChuckerInterceptor # OkHttp interceptor (Builder pattern) | ||
| ChuckerCollector # Data collection lifecycle | ||
| Chucker # Utility singleton (launch intent, notifications) | ||
| RetentionManager # Data cleanup policy | ||
| BodyDecoder # Interface for custom decoders (e.g., Protobuf) | ||
|
|
||
| internal/data/ # Persistence layer | ||
| entity/ # HttpTransaction (Room entity, 60+ fields) | ||
| room/ # ChuckerDatabase (v7, destructive migration) | ||
| repository/ # HttpTransactionRepository (LiveData queries) | ||
| har/ # HAR 1.2 export format classes | ||
|
|
||
| internal/support/ # Processing & utilities | ||
| RequestProcessor # Extracts request metadata + payload | ||
| ResponseProcessor # Extracts response metadata + payload (multicast via TeeSource) | ||
| NotificationHelper # Shows persistent notification with transaction count | ||
| *Sharable classes # Export as text/curl/HAR file | ||
| Stream utilities # TeeSource, LimitingSource, DepletingSource, ReportingSink | ||
|
|
||
| internal/ui/ # Android MVVM UI | ||
| MainActivity # Transaction list (singleTask, separate task affinity) | ||
| TransactionActivity # Detail view: Overview + Request + Response tabs | ||
| *ViewModel classes # LiveData from repository, filtering by code/path | ||
| ``` | ||
|
|
||
| ## How to Build & Test | ||
|
|
||
| ```bash | ||
| ./gradlew :library:test # Run all tests (JUnit 5 + Robolectric) | ||
| ./gradlew :library:lint # Lint (warnings = errors) | ||
| ./gradlew :library:assembleDebug # Build AAR | ||
| ``` | ||
|
|
||
| ## Data Flow | ||
|
|
||
| ``` | ||
| App makes HTTP call via OkHttp | ||
| β ChuckerInterceptor.intercept() | ||
| β RequestProcessor extracts headers, URL, body (with size limit + custom decoders) | ||
| β ChuckerCollector.onRequestSent() β Room DB insert | ||
| β chain.proceed(request) [actual network call] | ||
| β ResponseProcessor extracts status, headers, body (TeeSource for multicast) | ||
| β ChuckerCollector.onResponseReceived() β Room DB update + notification | ||
| β UI observes LiveData from Room β RecyclerView updates automatically | ||
| ``` | ||
|
mohitguptameesho marked this conversation as resolved.
|
||
|
|
||
| ## How to Make Changes | ||
|
|
||
| ### Adding a new field to display in the transaction detail screen | ||
|
|
||
| 1. If it's a new data field, add to `HttpTransaction` entity with `@ColumnInfo` | ||
| 2. Increment DB version in `ChuckerDatabase` (currently 7) β data wipes on upgrade, that's fine | ||
| 3. Populate in `RequestProcessor` or `ResponseProcessor` | ||
| 4. Add UI in the relevant fragment: | ||
| - `TransactionOverviewFragment` β metadata (URL, status, timing) | ||
| - `TransactionPayloadFragment` β headers and body content | ||
| 5. If it's a list field, add adapter item type in `TransactionPayloadAdapter` | ||
|
|
||
| ### Adding a new export format | ||
|
|
||
| 1. Create class implementing `Sharable` interface in `internal/support/` | ||
| 2. Add menu option in `TransactionActivity` menu | ||
| 3. Handle in `TransactionActivity.onOptionsItemSelected()` | ||
|
|
||
| ### Adding a new public API option | ||
|
|
||
| 1. Add Builder field + method in `ChuckerInterceptor` (return `this` for chaining) | ||
| 2. Pass to processor via constructor | ||
| 3. **Must mirror in library-no-op** β same method signature, empty body | ||
| 4. Run `./gradlew apiDump` to update `.api` files | ||
| 5. Add test in `ChuckerInterceptorTest` | ||
|
|
||
| ### Modifying the Room database | ||
|
|
||
| - Entity: `HttpTransaction` in `internal/data/entity/` | ||
| - DAO: `HttpTransactionDao` in `internal/data/room/` | ||
| - Database: `ChuckerDatabase` β uses `fallbackToDestructiveMigration()` (no migration files needed) | ||
| - All fields need `@ColumnInfo` annotation | ||
| - `HttpTransaction` is kept by ProGuard (`proguard-rules.pro`) β new fields are safe | ||
|
|
||
| ## Testing Patterns | ||
|
|
||
| ```kotlin | ||
| // Typical interceptor test structure | ||
| @ExtendWith(NoLoggerRule::class) | ||
| internal class ChuckerInterceptorTest { | ||
| @get:Rule val server = MockWebServer() | ||
|
|
||
| @Test | ||
| fun `descriptive test name in backticks`() { | ||
| // Use ClientFactory to create OkHttp client with Chucker | ||
| // Use ChuckerInterceptorDelegate to wrap and assert | ||
| // Use TestTransactionFactory for mock data | ||
| // Assert with Truth: assertThat(result).isEqualTo(expected) | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Key test utilities (in `src/test/.../util/`): | ||
| - `TestTransactionFactory` β creates mock HttpTransaction objects | ||
| - `ClientFactory` β OkHttp client setup variants | ||
| - `ChuckerInterceptorDelegate` β interceptor wrapper for assertions | ||
| - `NoLoggerRule` β suppresses Chucker logging in tests | ||
|
|
||
| ## Gotchas | ||
|
|
||
| - **Explicit API is strict** β every `public`/`internal`/`private` must be written out. Omitting it = compile error. | ||
| - **Resource prefix** β all resources must start with `chucker_`. Build fails otherwise. | ||
| - **Detekt limits** β max 16 method complexity, max 5 condition complexity, max 5 returns. `maxIssues: 1`. | ||
| - **`MainScope()` in ChuckerCollector is never cancelled** β create one collector and reuse it. | ||
| - **Manifest: MainActivity uses `singleTask` + separate task affinity** β this enables multi-window but may conflict with host app's task setup. | ||
| - **No RTL support** β lint check is disabled, UI is LTR only. | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add language specifier to code block for linter compliance.
The ASCII art architecture diagram should specify a language identifier (e.g.,
textorplaintext) to satisfy markdownlint MD040 rule.π Proposed fix
π Committable suggestion
π§° Tools
πͺ markdownlint-cli2 (0.22.1)
[warning] 7-7: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
π€ Prompt for AI Agents