Skip to content

Commit

Permalink
Feature flag for autofill site breakage reporting
Browse files Browse the repository at this point in the history
  • Loading branch information
CDRussell committed Jul 9, 2024
1 parent e5342b9 commit 3dc3d78
Show file tree
Hide file tree
Showing 8 changed files with 397 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2024 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.autofill.impl.reporting

import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.longPreferencesKey
import com.duckduckgo.autofill.impl.ui.credential.management.importpassword.desktopapp.ImportPasswordDesktopSync
import com.duckduckgo.di.scopes.AppScope
import com.squareup.anvil.annotations.ContributesBinding
import dagger.SingleInstanceIn
import javax.inject.Inject
import kotlinx.coroutines.flow.firstOrNull

interface AutofillSiteBreakageReportingDataStore {

suspend fun startUserJourney()
suspend fun getUserJourneyStartTime(): Long?
suspend fun clearUserJourneyStartTime()
}

@SingleInstanceIn(AppScope::class)
@ContributesBinding(AppScope::class)
class AutofillSiteBreakageReportingDataStoreImpl @Inject constructor(
@ImportPasswordDesktopSync private val store: DataStore<Preferences>,
) : AutofillSiteBreakageReportingDataStore {

private val userJourneyStartTime = longPreferencesKey("user_journey_started_timestamp")

override suspend fun startUserJourney() {
store.edit {
it[userJourneyStartTime] = System.currentTimeMillis()
}
}

override suspend fun getUserJourneyStartTime(): Long? {
return store.data.firstOrNull()?.get(userJourneyStartTime)
}

override suspend fun clearUserJourneyStartTime() {
store.edit {
it.remove(userJourneyStartTime)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2024 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.autofill.impl.reporting.remoteconfig

import com.duckduckgo.autofill.store.reporting.AutofillSiteBreakageReportingEntity
import com.duckduckgo.autofill.store.reporting.AutofillSiteBreakageReportingFeatureRepository
import com.duckduckgo.di.scopes.AppScope
import com.duckduckgo.feature.toggles.api.FeatureExceptions
import com.duckduckgo.feature.toggles.api.RemoteFeatureStoreNamed
import com.squareup.anvil.annotations.ContributesBinding
import javax.inject.Inject

@ContributesBinding(AppScope::class)
@RemoteFeatureStoreNamed(AutofillSiteBreakageReportingFeature::class)
class AutofillSiteBreakageReportingExceptionsPersister @Inject constructor(
private val repository: AutofillSiteBreakageReportingFeatureRepository,
) : FeatureExceptions.Store {
override fun insertAll(exception: List<FeatureExceptions.FeatureException>) {
repository.updateAllExceptions(
exception.map { AutofillSiteBreakageReportingEntity(domain = it.domain, reason = it.reason.orEmpty()) },
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2024 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.autofill.impl.reporting.remoteconfig

import com.duckduckgo.feature.toggles.api.Toggle

/**
* This is the class that represents the feature flag for offering to report Autofill breakages
*/
interface AutofillSiteBreakageReportingFeature {
/**
* @return `true` when the remote config has the global "autofillBreakageReporter" feature flag enabled
*
* If the remote feature is not present defaults to `false`
*/

@Toggle.DefaultValue(false)
fun self(): Toggle
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2024 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.autofill.impl.reporting.remoteconfig

import android.content.Context
import androidx.room.Room
import com.duckduckgo.app.di.AppCoroutineScope
import com.duckduckgo.app.di.IsMainProcess
import com.duckduckgo.autofill.store.reporting.ALL_MIGRATIONS
import com.duckduckgo.autofill.store.reporting.AutofillSiteBreakageReportingDatabase
import com.duckduckgo.autofill.store.reporting.AutofillSiteBreakageReportingFeatureRepository
import com.duckduckgo.autofill.store.reporting.AutofillSiteBreakageReportingFeatureRepositoryImpl
import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.di.scopes.AppScope
import com.squareup.anvil.annotations.ContributesTo
import dagger.Module
import dagger.Provides
import dagger.SingleInstanceIn
import kotlinx.coroutines.CoroutineScope

@Module
@ContributesTo(AppScope::class)
class AutofillSiteBreakageReportingModule {

@SingleInstanceIn(AppScope::class)
@Provides
fun repository(
database: AutofillSiteBreakageReportingDatabase,
@AppCoroutineScope appCoroutineScope: CoroutineScope,
dispatcherProvider: DispatcherProvider,
@IsMainProcess isMainProcess: Boolean,
): AutofillSiteBreakageReportingFeatureRepository {
return AutofillSiteBreakageReportingFeatureRepositoryImpl(database, appCoroutineScope, dispatcherProvider, isMainProcess)
}

@Provides
@SingleInstanceIn(AppScope::class)
fun database(context: Context): AutofillSiteBreakageReportingDatabase {
return Room.databaseBuilder(context, AutofillSiteBreakageReportingDatabase::class.java, "autofillSiteBreakageReporting.db")
.fallbackToDestructiveMigration()
.addMigrations(*ALL_MIGRATIONS)
.build()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2024 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.autofill.impl.reporting.remoteconfig

import com.duckduckgo.app.di.AppCoroutineScope
import com.duckduckgo.autofill.impl.reporting.AutofillSiteBreakageReportingDataStore
import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.di.scopes.AppScope
import com.duckduckgo.feature.toggles.api.FeatureSettings
import com.duckduckgo.feature.toggles.api.RemoteFeatureStoreNamed
import com.squareup.anvil.annotations.ContributesBinding
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.json.JSONObject

@ContributesBinding(AppScope::class)
@RemoteFeatureStoreNamed(AutofillSiteBreakageReportingFeature::class)
class AutofillSiteBreakageReportingRemoteSettingsPersister @Inject constructor(
private val dataStore: AutofillSiteBreakageReportingDataStore,
@AppCoroutineScope private val appCoroutineScope: CoroutineScope,
private val dispatchers: DispatcherProvider,
) : FeatureSettings.Store {

override fun store(jsonString: String) {
appCoroutineScope.launch(dispatchers.io()) {
val json = JSONObject(jsonString)

"installedDays".let {
val installDays = if (json.has(it)) {
json.getInt(it)
} else {
Int.MAX_VALUE
}

// dataStore.updateMaximumPermittedDaysSinceInstallation(installDays)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2024 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.autofill.impl.reporting.remoteconfig

import com.duckduckgo.anvil.annotations.ContributesRemoteFeature
import com.duckduckgo.di.scopes.AppScope

@ContributesRemoteFeature(
scope = AppScope::class,
boundType = AutofillSiteBreakageReportingFeature::class,
featureName = "autofillBreakageReporter",
settingsStore = AutofillSiteBreakageReportingRemoteSettingsPersister::class,
exceptionsStore = AutofillSiteBreakageReportingExceptionsPersister::class,
)
@Suppress("unused")
private interface UnusedAutofillSiteBreakageReportingRemoteFeatureCodegenTrigger
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2024 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.autofill.store.reporting

import androidx.room.Dao
import androidx.room.Database
import androidx.room.Entity
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.PrimaryKey
import androidx.room.Query
import androidx.room.RoomDatabase
import androidx.room.Transaction
import androidx.room.migration.Migration

@Database(
exportSchema = true,
version = 1,
entities = [
AutofillSiteBreakageReportingEntity::class,
],
)
abstract class AutofillSiteBreakageReportingDatabase : RoomDatabase() {
abstract fun dao(): AutofillSiteBreakageReportingDao
}

@Entity(tableName = "autofill_site_breakage_reporting")
data class AutofillSiteBreakageReportingEntity(
@PrimaryKey val domain: String,
val reason: String,
)

val ALL_MIGRATIONS = emptyArray<Migration>()

@Dao
abstract class AutofillSiteBreakageReportingDao {

@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun insertAll(domains: List<AutofillSiteBreakageReportingEntity>)

@Transaction
open fun updateAll(domains: List<AutofillSiteBreakageReportingEntity>) {
deleteAll()
insertAll(domains)
}

@Query("select * from autofill_site_breakage_reporting where domain = :domain")
abstract fun get(domain: String): AutofillSiteBreakageReportingEntity

@Query("select * from autofill_site_breakage_reporting")
abstract fun getAll(): List<AutofillSiteBreakageReportingEntity>

@Query("delete from autofill_site_breakage_reporting")
abstract fun deleteAll()
}
Loading

0 comments on commit 3dc3d78

Please sign in to comment.