diff --git a/consentium/build.gradle.kts b/consentium/build.gradle.kts index d4d5e77..e020f67 100644 --- a/consentium/build.gradle.kts +++ b/consentium/build.gradle.kts @@ -3,6 +3,7 @@ plugins { alias(libs.plugins.kotlin.android) alias(libs.plugins.hilt) alias(libs.plugins.ksp) + alias(libs.plugins.serialization) } android { @@ -43,6 +44,22 @@ dependencies { implementation(libs.hilt.android) ksp(libs.hilt.compiler) + // Serialization + implementation(libs.serializationJson) + + // Retrofit + api(libs.retrofit) + implementation(libs.retrofitConverter) + implementation(libs.logging.interceptor) + + // OkHttp + api(libs.okhttp) + implementation(platform(libs.okhttp.bom)) + implementation(libs.okhttp) + + // Timber + implementation(libs.timber) + // Test testImplementation(libs.test.junit) androidTestImplementation(libs.androidx.junit) diff --git a/consentium/src/debug/kotlin/fr/openium/consentium/DebugNetworkModule.kt b/consentium/src/debug/kotlin/fr/openium/consentium/DebugNetworkModule.kt new file mode 100644 index 0000000..c09d32a --- /dev/null +++ b/consentium/src/debug/kotlin/fr/openium/consentium/DebugNetworkModule.kt @@ -0,0 +1,37 @@ +package fr.openium.consentium + +import android.content.Context +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import fr.openium.consentium.data.di.ConsentiumUrl +import fr.openium.consentium.data.di.NetworkModule +import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import timber.log.Timber + +@Module +@InstallIn(SingletonComponent::class) +class DebugNetworkModule { + + @Provides + fun provideOkHttpBuilder( + @ApplicationContext context: Context, + ): OkHttpClient.Builder = + NetworkModule.standardOkHttpBuilder(context) + .addNetworkInterceptor(HttpLoggingInterceptor { message -> + Timber.tag("OkHttp") + Timber.v(message) + }.apply { + level = HttpLoggingInterceptor.Level.BODY + }) + + @ConsentiumUrl + @Provides + fun okHttpUrlConsentium(@ApplicationContext context: Context): HttpUrl = + context.getString(R.string.backend_url).toHttpUrl() +} \ No newline at end of file diff --git a/consentium/src/debug/res/values/strings.xml b/consentium/src/debug/res/values/strings.xml new file mode 100644 index 0000000..c04ccc1 --- /dev/null +++ b/consentium/src/debug/res/values/strings.xml @@ -0,0 +1,4 @@ + + + https://consentium-api-dev.openium.fr/api/v1/app + \ No newline at end of file diff --git a/consentium/src/main/java/fr/openium/consentium/data/di/NetworkModule.kt b/consentium/src/main/java/fr/openium/consentium/data/di/NetworkModule.kt new file mode 100644 index 0000000..3680992 --- /dev/null +++ b/consentium/src/main/java/fr/openium/consentium/data/di/NetworkModule.kt @@ -0,0 +1,78 @@ +package fr.openium.consentium.data.di + +import android.content.Context +import dagger.Lazy +import dagger.Module +import dagger.Provides +import dagger.Reusable +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import fr.openium.consentium.data.remote.ConsentiumApi +import kotlinx.serialization.json.Json +import okhttp3.Cache +import okhttp3.HttpUrl +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient +import retrofit2.Retrofit +import retrofit2.converter.kotlinx.serialization.asConverterFactory +import java.util.concurrent.TimeUnit +import javax.inject.Qualifier +import javax.inject.Singleton + +private val json by lazy { + Json { + isLenient = true + ignoreUnknownKeys = true + } +} + +@Module +@InstallIn(SingletonComponent::class) +internal object NetworkModule { + + @OkHttpClientDefault + @Provides + @Singleton + fun okHttpClientDefault( + okHttpBuilder: OkHttpClient.Builder, + ): OkHttpClient = okHttpBuilder.build() + + fun standardOkHttpBuilder( + context: Context, + ): OkHttpClient.Builder { + val cacheSize = (20 * 1024 * 1024).toLong() // 20 MiB + val cache = Cache(context.cacheDir, cacheSize) + return OkHttpClient.Builder() + .cache(cache) + .connectTimeout(10, TimeUnit.SECONDS) + .readTimeout(10, TimeUnit.SECONDS) + .writeTimeout(10, TimeUnit.SECONDS) + } + + @Reusable + @Provides + fun provideConsentiumApi( + @ConsentiumUrl url: HttpUrl, + @OkHttpClientDefault okHttpClient: Lazy, + ): ConsentiumApi = + createRetrofit( + url, + okHttpClient + ).create(ConsentiumApi::class.java) + + private fun createRetrofit(url: HttpUrl, okHttpClient: Lazy): Retrofit = + Retrofit.Builder() + .callFactory { request -> + okHttpClient.get().newCall(request) + }.addConverterFactory(json.asConverterFactory("application/json".toMediaType())) + .baseUrl(url) + .build() +} + +@Qualifier +@Retention(AnnotationRetention.BINARY) +internal annotation class ConsentiumUrl + +@Qualifier +@Retention(AnnotationRetention.BINARY) +internal annotation class OkHttpClientDefault diff --git a/consentium/src/main/java/fr/openium/consentium/data/remote/ConsentiumApi.kt b/consentium/src/main/java/fr/openium/consentium/data/remote/ConsentiumApi.kt new file mode 100644 index 0000000..4f3847d --- /dev/null +++ b/consentium/src/main/java/fr/openium/consentium/data/remote/ConsentiumApi.kt @@ -0,0 +1,25 @@ +package fr.openium.consentium.data.remote + +import fr.openium.consentium.data.remote.model.GetConsent +import fr.openium.consentium.data.remote.model.PatchConsent +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.PATCH + + +internal interface ConsentiumApi { + + @GET("/consents") + suspend fun getConsents( + applicationId: String, + installationId: String, + ): GetConsent.GetConsentPayloadDTO + + @PATCH("/consents") + suspend fun setConsents( + applicationId: String, + installationId: String, + @Body patchConsent: PatchConsent.PatchConsentPayloadDTO, + ): Any + +} \ No newline at end of file diff --git a/consentium/src/main/java/fr/openium/consentium/data/remote/model/GetConsentPayloadDTO.kt b/consentium/src/main/java/fr/openium/consentium/data/remote/model/GetConsentPayloadDTO.kt new file mode 100644 index 0000000..3206996 --- /dev/null +++ b/consentium/src/main/java/fr/openium/consentium/data/remote/model/GetConsentPayloadDTO.kt @@ -0,0 +1,44 @@ +package fr.openium.consentium.data.remote.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +internal sealed interface GetConsent { + + @Serializable + data class GetConsentPayloadDTO( + @SerialName("installationId") val installationId: String, + @SerialName("purposes") val purposes: List? = null, + @SerialName("isValid") val isValid: Boolean, + ) + + @Serializable + data class PurposeDTO( + @SerialName("identifier") val identifier: String, + @SerialName("isRequired") val isRequired: Boolean, + @SerialName("isAccepted") val isAccepted: PurposeStatus, + @SerialName("vendors") val vendors: List? = null, + ) + + @Serializable + data class VendorDTO( + @SerialName("identifier") val identifier: String, + @SerialName("isRequired") val isRequired: Boolean, + @SerialName("isAccepted") val isAccepted: Boolean, + ) + + @Serializable + enum class PurposeStatus { + @SerialName("ACCEPTED") + ACCEPTED, + + @SerialName("REJECTED") + REJECTED, + + @SerialName("NOT_DEFINED") + NOT_DEFINED, + } + +} + + diff --git a/consentium/src/main/java/fr/openium/consentium/data/remote/model/PatchConsentPayloadDTO.kt b/consentium/src/main/java/fr/openium/consentium/data/remote/model/PatchConsentPayloadDTO.kt new file mode 100644 index 0000000..f7a6202 --- /dev/null +++ b/consentium/src/main/java/fr/openium/consentium/data/remote/model/PatchConsentPayloadDTO.kt @@ -0,0 +1,27 @@ +package fr.openium.consentium.data.remote.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +internal sealed interface PatchConsent { + + @Serializable + data class PatchConsentPayloadDTO( + @SerialName("installationId") val installationId: String, + @SerialName("purposes") val purposes: List? = null, + ) + + @Serializable + data class PurposeDTO( + @SerialName("identifier") val identifier: String, + @SerialName("isAccepted") val isAccepted: Boolean, + @SerialName("vendors") val vendors: List? = null, + ) + + @Serializable + data class VendorDTO( + @SerialName("identifier") val identifier: String, + @SerialName("isAccepted") val isAccepted: Boolean, + ) + +} diff --git a/consentium/src/main/java/fr/openium/consentium/data/repository/ConsentiumRepository.kt b/consentium/src/main/java/fr/openium/consentium/data/repository/ConsentiumRepository.kt new file mode 100644 index 0000000..f553d83 --- /dev/null +++ b/consentium/src/main/java/fr/openium/consentium/data/repository/ConsentiumRepository.kt @@ -0,0 +1,13 @@ +package fr.openium.consentium.data.repository + +import fr.openium.consentium.data.local.ConsentiumDataStore +import fr.openium.consentium.data.remote.ConsentiumApi +import javax.inject.Inject + +internal class ConsentiumRepository @Inject constructor( + private val consentiumApi: ConsentiumApi, + private val consentiumDataStore: ConsentiumDataStore, +) { + + +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b58fe56..363d9b1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,6 +26,17 @@ timber = "5.0.1" # Material material = "1.12.0" +# Serialization +serialization = "2.0.21" +jsonSerialization = "1.7.3" + +# Retrofit +retrofit = "2.11.0" +loggingInterceptor = "4.12.0" + +# Okhttp +okhttpBom = "4.12.0" + # Test junit = "4.13.2" espressoCore = "3.6.1" @@ -94,6 +105,18 @@ androidx-navigation-compose = { group = "androidx.navigation", name = "navigatio # Preferences DataStore preferencesDataStore = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "preferencesDataStore" } +# Json serialization +serializationJson = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "jsonSerialization" } + +# Retrofit +retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" } +retrofitConverter = { group = "com.squareup.retrofit2", name = "converter-kotlinx-serialization", version.ref = "retrofit" } +logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "loggingInterceptor" } + +# Okhttp +okhttp-bom = { module = "com.squareup.okhttp3:okhttp-bom", version.ref = "okhttpBom" } +okhttp = { module = "com.squareup.okhttp3:okhttp" } + # Test test-junit = { group = "junit", name = "junit", version.ref = "junit" } test-androidx-junit = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "junitExtVersion" } @@ -107,9 +130,7 @@ kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "ko ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } android-library = { id = "com.android.library", version.ref = "agp" } hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } -kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } - - +serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "serialization" } [bundles] androidx = ["androidx-core-ktx", "androidx-activity-compose"]