Skip to content
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

Feature management for #88 #141

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 46 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,32 +78,52 @@ The main Chucker activity is launched in its own task, allowing it to be display

## Configure ๐ŸŽจ

You can customize chucker providing an instance of a `ChuckerCollector`:
You can customize Chucker by calling the DSL `configureChucker`.
It may be used to disable one of the features (HTTP or Errors).

```kotlin
// Create the Collector
val chuckerCollector = ChuckerCollector(
context = this,
// Toggles visibility of the push notification
showNotification = true,
// Allows to customize the retention period of collected data
configureChucker {
http {
enabled = true
showNotification = true
retentionPeriod = RetentionManager.Period.ONE_HOUR
)

// Create the Interceptor
val chuckerInterceptor = ChuckerInterceptor(
context = this,
// The previously created Collector
collector = chuckerCollector,
// The max body content length, after this responses will be truncated.
maxContentLength = 250000L,
// List of headers to obfuscate in the Chucker UI
headersToRedact = listOf("Auth-Token"))

// Don't forget to plug the ChuckerInterceptor inside the OkHttpClient
val client = OkHttpClient.Builder()
.addInterceptor(chuckerInterceptor)
.build()
maxContentLength = 250000L
headers {
redact("Authorization")
redact("Auth-Token")
redact("User-Session")
}
}
error {
enabled = true
showNotification = true
}
}
```

### Configure for Java

```java
HashSet<String> headersToRedact = new HashSet<>();
headersToRedact.add("Authorization");
headersToRedact.add("Auth-Token");
headersToRedact.add("User-Session");

List<TabFeature> features = Arrays.asList(
new HttpFeature(
true,
true,
RetentionManager.Period.ONE_HOUR,
DEFAULT_MAX_CONTENT_LENGTH,
headersToRedact
),
new ErrorsFeature(
true,
true
)
);

ChuckerJavaConfig.configure(features);
```

### Throwables โ˜„๏ธ
Expand All @@ -113,7 +133,7 @@ Chucker supports also collecting and displaying **Throwables** of your applicati
```kotlin
try {
// Do something risky
} catch (IOException exception) {
} catch (exception: IOException) {
olivierperez marked this conversation as resolved.
Show resolved Hide resolved
chuckerCollector.onError("TAG", exception)
}
```
Expand All @@ -127,8 +147,8 @@ It is intended for **use during development**, and not in release builds or othe
You can redact headers that contain sensitive information by calling `redactHeader(String)` on the `ChuckerInterceptor`.

```kotlin
interceptor.redactHeader("Auth-Token");
interceptor.redactHeader("User-Session");
olivierperez marked this conversation as resolved.
Show resolved Hide resolved
interceptor.redactHeader("Auth-Token")
interceptor.redactHeader("User-Session")
```

## Migrating ๐Ÿš—
Expand Down
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ buildscript {
retrofitVersion = '2.5.0'
roomVersion = '2.1.0'
supportLibVersion = '1.1.0-rc01'
fragmentVersion = '1.1.0'
}

repositories {
Expand Down
1 change: 1 addition & 0 deletions library-no-op/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ artifacts {
dependencies {
implementation "com.squareup.okhttp3:okhttp:$okhttp3Version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
implementation "androidx.fragment:fragment:$fragmentVersion"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe we could think about some other way of implementing feature management, which would allow to not include this dependency?

}

apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ import android.content.Context
/**
* No-op implementation.
*/
class ChuckerCollector @JvmOverloads constructor(
context: Context,
var showNotification: Boolean = true,
var retentionPeriod: RetentionManager.Period = RetentionManager.Period.ONE_WEEK
class ChuckerCollector(
context: Context
) {
@Deprecated("This constructor will disappear in a following version.")
constructor(
context: Context,
showNotification: Boolean,
retentionPeriod: RetentionManager.Period
) : this(context)

fun onError(obj: Any?, obj2: Any?) {
// Empty method for the library-no-op artifact
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@ import okhttp3.Response
*/
class ChuckerInterceptor @JvmOverloads constructor(
context: Context,
collector: Any? = null,
maxContentLength: Any? = null,
headersToRedact: Any? = null
collector: Any? = null
) : Interceptor {

@Deprecated("This constructor will disappear in a following version.")
constructor(
context: Context,
collector: Any? = null,
maxContentLength: Any? = null,
headersToRedact: Any? = null
) : this(context, collector)

fun redactHeaders(vararg names: String): ChuckerInterceptor {
return this
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import android.content.Context
/**
* No-op implementation.
*/
class RetentionManager @JvmOverloads constructor(
context: Context,
retentionPeriod: Any? = null
class RetentionManager(
context: Context
) {

@Synchronized
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@file:JvmName("ChuckerJavaConfig")

package com.chuckerteam.chucker.api.config

fun configure(features: List<TabFeature>) {
// Empty method for the library-no-op artifact
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.chuckerteam.chucker.api.config

import android.content.Context
import com.chuckerteam.chucker.api.internal.EmptyFragment

class ErrorsFeature(
override var enabled: Boolean,
var showNotification: Boolean
) : TabFeature {
override val name: Int = 0

override val id: Int = 0

override fun newFragment() = EmptyFragment()

override fun dismissNotification(context: Context) {
// Empty method for the library-no-op artifact
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.chuckerteam.chucker.api.config

import android.content.Context
import com.chuckerteam.chucker.api.RetentionManager
import com.chuckerteam.chucker.api.internal.EmptyFragment

class HttpFeature(
override var enabled: Boolean,
var showNotification: Boolean,
var retentionPeriod: RetentionManager.Period,
var maxContentLength: Long,
var headersToRedact: MutableSet<String>
) : TabFeature {
override val name: Int = 0

override val id: Int = 0

override fun newFragment() = EmptyFragment()

override fun dismissNotification(context: Context) {
// Empty method for the library-no-op artifact
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.chuckerteam.chucker.api.config

import android.content.Context
import androidx.fragment.app.Fragment

interface TabFeature {
val name: Int
val id: Int
var enabled: Boolean
fun newFragment(): Fragment
fun dismissNotification(context: Context)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.chuckerteam.chucker.api.dsl

import com.chuckerteam.chucker.api.RetentionManager

const val DEFAULT_MAX_CONTENT_LENGTH = 250000L

@DslMarker
annotation class ChuckerConfig

@ChuckerConfig
fun configureChucker(config: ChuckerConfigBuilder.() -> Unit) = Unit

@ChuckerConfig
class ChuckerConfigBuilder {

@ChuckerConfig
fun http(block: HttpFeatureBuilder.() -> Unit) = Unit

@ChuckerConfig
fun error(block: ErrorsFeatureBuilder.() -> Unit) = Unit

fun build() = Unit
}

@ChuckerConfig
class HttpFeatureBuilder {
var enabled: Boolean = true
var showNotification: Boolean = true
var retentionPeriod: RetentionManager.Period = RetentionManager.Period.ONE_WEEK
var maxContentLength: Long = 0
internal var headersToRedact: MutableSet<String> = mutableSetOf()

@ChuckerConfig
fun headers(redactHeaders: RedactHeaders.() -> Unit) = Unit
}

@ChuckerConfig
class RedactHeaders {
@ChuckerConfig
fun redact(header: String) = Unit
}

@ChuckerConfig
class ErrorsFeatureBuilder {
var enabled: Boolean = true
var showNotification: Boolean = true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.chuckerteam.chucker.api.internal

import androidx.fragment.app.Fragment

class EmptyFragment : Fragment()
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.chuckerteam.chucker.api

import android.content.Context
import com.chuckerteam.chucker.api.config.ErrorsFeature
import com.chuckerteam.chucker.api.config.HttpFeature
import com.chuckerteam.chucker.internal.data.entity.HttpTransaction
import com.chuckerteam.chucker.internal.data.entity.RecordedThrowable
import com.chuckerteam.chucker.internal.data.repository.RepositoryProvider
import com.chuckerteam.chucker.internal.support.FeatureManager
import com.chuckerteam.chucker.internal.support.NotificationHelper

/**
Expand All @@ -12,19 +15,29 @@ import com.chuckerteam.chucker.internal.support.NotificationHelper
* provide it to
*
* @param context An Android Context
* @param showNotification Control whether a notification is shown while HTTP activity
* is recorded.
* @param retentionManager Set the retention period for HTTP transaction data captured
* by this collector. The default is one week.
*/
class ChuckerCollector @JvmOverloads constructor(
context: Context,
var showNotification: Boolean = true,
retentionPeriod: RetentionManager.Period = RetentionManager.Period.ONE_WEEK
class ChuckerCollector(
context: Context
) {
private val retentionManager: RetentionManager = RetentionManager(context, retentionPeriod)
@Deprecated("This constructor will disappear in a following version.")
constructor(
context: Context,
showNotification: Boolean,
retentionPeriod: RetentionManager.Period
) : this(context) {
// This 3 lines are here to avoid breaking changes in the constructor signature
// They will disappear when we will make the breaking changes.
httpFeature.showNotification = showNotification
httpFeature.retentionPeriod = retentionPeriod
errorsFeature.showNotification = showNotification
}

private val retentionManager: RetentionManager = RetentionManager(context)
private val notificationHelper: NotificationHelper = NotificationHelper(context)

private val httpFeature: HttpFeature = FeatureManager.find()
private val errorsFeature: ErrorsFeature = FeatureManager.find()

init {
RepositoryProvider.initialize(context)
}
Expand All @@ -35,9 +48,11 @@ class ChuckerCollector @JvmOverloads constructor(
* @param throwable The triggered [Throwable]
*/
fun onError(tag: String, throwable: Throwable) {
if (!errorsFeature.enabled) return

val recordedThrowable = RecordedThrowable(tag, throwable)
RepositoryProvider.throwable().saveThrowable(recordedThrowable)
if (showNotification) {
if (errorsFeature.showNotification) {
notificationHelper.show(recordedThrowable)
}
retentionManager.doMaintenance()
Expand All @@ -48,8 +63,10 @@ class ChuckerCollector @JvmOverloads constructor(
* @param transaction The HTTP transaction sent
*/
internal fun onRequestSent(transaction: HttpTransaction) {
if (!httpFeature.enabled) return
Copy link
Collaborator

Choose a reason for hiding this comment

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

I suggest to add braces here for readability and to match other code lines, like below where condition with only one line in it has braces.

Copy link
Member Author

Choose a reason for hiding this comment

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

I use braces when there is something to do.
My point of view is this is an early return, in order to do nothing. If I do nothing I have no braces ๐Ÿคทโ€โ™‚

What do you think of that?

Copy link
Member

Choose a reason for hiding this comment

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

I use braces when there is something to do.

Agree with @olivierperez
Also the style guide here https://developer.android.com/kotlin/style-guide#braces allows single-line if statements like:

if (condition) effect


RepositoryProvider.transaction().insertTransaction(transaction)
if (showNotification) {
if (httpFeature.showNotification) {
notificationHelper.show(transaction)
}
retentionManager.doMaintenance()
Expand All @@ -61,8 +78,10 @@ class ChuckerCollector @JvmOverloads constructor(
* @param transaction The sent HTTP transaction completed with the response
*/
internal fun onResponseReceived(transaction: HttpTransaction) {
if (!httpFeature.enabled) return
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same suggestion about braces as above.


val updated = RepositoryProvider.transaction().updateTransaction(transaction)
if (showNotification && updated > 0) {
if (httpFeature.showNotification && updated > 0) {
notificationHelper.show(transaction)
}
}
Expand Down
Loading