Skip to content

Observe authenticated user to update profile #41

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
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import com.couchbase.learningpath.data.DatabaseManager
import com.couchbase.learningpath.data.audits.AuditRepositoryDb
import com.couchbase.learningpath.data.project.ProjectRepositoryDb
import com.couchbase.learningpath.data.stockItem.StockItemRepositoryDb
import com.couchbase.learningpath.data.userprofile.UserProfileRepository
import com.couchbase.learningpath.data.userprofile.UserProfileRepositoryDb
import com.couchbase.learningpath.data.warehouse.WarehouseRepositoryDb
import com.couchbase.learningpath.models.*
import com.couchbase.learningpath.services.MockAuthenticationService
import com.couchbase.lite.CouchbaseLiteException
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.*
import org.junit.Assert.*
Expand All @@ -34,7 +35,7 @@ class DatabaseIntegrationTests {
private lateinit var warehouseRepository: WarehouseRepositoryDb
private lateinit var auditRepository: AuditRepositoryDb
private lateinit var stockItemRepository: StockItemRepositoryDb
private lateinit var userProfileRepository: UserProfileRepository
private lateinit var userProfileRepository: UserProfileRepositoryDb

//test users
private lateinit var user1: User
Expand Down Expand Up @@ -80,14 +81,16 @@ class DatabaseIntegrationTests {
databaseManager.deleteDatabases()
databaseManager.initializeDatabases(user1)

authenticationService = MockAuthenticationService()
val isAuth = authenticationService.authenticatedUser(user1.username, user1.password)
authenticationService = MockAuthenticationService(databaseManager)
val isAuth = runBlocking {
authenticationService.authenticatedUser(user1.username, user1.password)
}

//arrange repositories
auditRepository = AuditRepositoryDb(authenticationService, databaseManager)
stockItemRepository = StockItemRepositoryDb(databaseManager)
warehouseRepository = WarehouseRepositoryDb(databaseManager)
userProfileRepository = UserProfileRepository(databaseManager)
userProfileRepository = UserProfileRepositoryDb(databaseManager)
projectRepository = ProjectRepositoryDb(
authenticationService = authenticationService,
warehouseRepository = warehouseRepository,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ import org.koin.core.logger.Level
import org.koin.core.module.Module
import org.koin.dsl.module

import com.couchbase.learningpath.data.KeyValueRepository
import com.couchbase.learningpath.data.audits.AuditRepository
import com.couchbase.learningpath.data.audits.AuditRepositoryDb
import com.couchbase.learningpath.data.project.ProjectRepository
import com.couchbase.learningpath.data.project.ProjectRepositoryDb
import com.couchbase.learningpath.data.stockItem.StockItemRepository
import com.couchbase.learningpath.data.stockItem.StockItemRepositoryDb
import com.couchbase.learningpath.data.userprofile.UserProfileRepository
import com.couchbase.learningpath.data.userprofile.UserProfileRepositoryDb
import com.couchbase.learningpath.data.warehouse.WarehouseRepository
import com.couchbase.learningpath.data.warehouse.WarehouseRepositoryDb
import com.couchbase.learningpath.services.AuthenticationService
Expand Down Expand Up @@ -74,7 +74,7 @@ class InventoryApplication
singleOf(::DatabaseManager)
singleOf(::MockAuthenticationService) bind AuthenticationService::class
singleOf(::ReplicatorServiceDb) bind ReplicatorService::class
singleOf(::UserProfileRepository) bind KeyValueRepository::class
singleOf(::UserProfileRepositoryDb) bind UserProfileRepository::class
singleOf(::WarehouseRepositoryDb) bind WarehouseRepository::class
singleOf(::ProjectRepositoryDb) bind ProjectRepository::class
singleOf(::StockItemRepositoryDb) bind StockItemRepository::class
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ fun InventoryNavGraph(
navController: NavHostController = rememberNavController(),
scaffoldState: ScaffoldState = rememberScaffoldState(),
scope: CoroutineScope = rememberCoroutineScope(),
userProfileViewModel: UserProfileViewModel,
startDestination: String = MainDestinations.LOGIN_ROUTE) {
val actions = remember(navController) { MainActions(navController) }
NavHost(navController = navController,
Expand Down Expand Up @@ -192,7 +193,7 @@ fun InventoryNavGraph(
UserProfileView(
openDrawer = openDrawer,
scaffoldState = scaffoldState,
viewModel = getViewModel<UserProfileViewModel>())
viewModel = userProfileViewModel)
}

composable(MainDestinations.DEVELOPER_ROUTE){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ package com.couchbase.learningpath.data

interface KeyValueRepository {

fun inventoryDatabaseName(): String
fun inventoryDatabaseLocation(): String?

suspend fun count(): Int
suspend fun get(currentUser: String): Map<String, Any>
suspend fun save(data: Map<String, Any>) : Boolean
suspend fun get(key: String): Map<String, Any?>
suspend fun save(data: Map<String, Any?>) : Boolean
}
Original file line number Diff line number Diff line change
@@ -1,101 +1,10 @@
package com.couchbase.learningpath.data.userprofile

import com.couchbase.lite.CouchbaseLiteException
import com.couchbase.lite.MutableDocument

import com.couchbase.learningpath.data.DatabaseManager
import com.couchbase.learningpath.data.KeyValueRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class UserProfileRepository(
private val databaseManager: DatabaseManager
) : KeyValueRepository {
private val userProfileType = "user"

override fun inventoryDatabaseName(): String {
return databaseManager.currentInventoryDatabaseName
}

override fun inventoryDatabaseLocation(): String? {
return databaseManager.inventoryDatabase?.path
}

override suspend fun get(currentUser: String): Map<String, Any> {
return withContext(Dispatchers.IO) {
val results = HashMap<String, Any>() // <1>
results["email"] = currentUser as Any // <2>

val database = databaseManager.inventoryDatabase
database?.let { db ->
val documentId = getCurrentUserDocumentId(currentUser)
val doc = db.getDocument(documentId) // <3>
if (doc != null) {
if (doc.contains("givenName")) { // <4>
results["givenName"] = doc.getString("givenName") as Any // <4>
}
if (doc.contains("surname")) { // <4>
results["surname"] = doc.getString("surname") as Any // <4>
}
if (doc.contains("jobTitle")) { // <4>
results["jobTitle"] = doc.getString("jobTitle") as Any // <4>
}
if (doc.contains("team")) { // <4>
results["team"] = doc.getString("team") as Any // <4>
}
if (doc.contains("imageData")) { // <4>
results["imageData"] = doc.getBlob("imageData") as Any // <4>
}
}
}
return@withContext results // <5>
}
}

override suspend fun save(data: Map<String, Any>): Boolean {
return withContext(Dispatchers.IO) {
val email = data["email"] as String
val documentId = getCurrentUserDocumentId(email)
val mutableDocument = MutableDocument(documentId, data)
try {
val database = databaseManager.inventoryDatabase
database?.save(mutableDocument)
} catch (e: CouchbaseLiteException) {
android.util.Log.e(e.message, e.stackTraceToString())
return@withContext false
}
return@withContext true
}
}

override suspend fun count(): Int {
return withContext(Dispatchers.IO) {
val database = databaseManager.inventoryDatabase
database?.let { db ->
val query = "SELECT COUNT(*) AS count FROM _ WHERE documentType='$userProfileType'"
val results = db.createQuery(query).execute().allResults()
return@withContext results[0].getInt("count")
}
return@withContext 0
}
}

suspend fun delete(documentId: String): Boolean {
return withContext(Dispatchers.IO) {
var result = false
val database = databaseManager.inventoryDatabase
database?.let { db ->
val document = db.getDocument(documentId)
document?.let {
db.delete(it)
result = true
}
}
return@withContext result
}
}
interface UserProfileRepository : KeyValueRepository {

private fun getCurrentUserDocumentId(currentUser: String): String {
return "user::${currentUser}"
}
fun inventoryDatabaseName(): String
fun inventoryDatabaseLocation(): String?
suspend fun delete(documentId: String): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.couchbase.learningpath.data.userprofile

import com.couchbase.lite.CouchbaseLiteException
import com.couchbase.lite.MutableDocument

import com.couchbase.learningpath.data.DatabaseManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class UserProfileRepositoryDb(
private val databaseManager: DatabaseManager
) : UserProfileRepository {
private val userProfileType = "user"

override fun inventoryDatabaseName(): String {
return databaseManager.currentInventoryDatabaseName
}

override fun inventoryDatabaseLocation(): String? {
return databaseManager.inventoryDatabase?.path
}

override suspend fun get(key: String): Map<String, Any?> {
return withContext(Dispatchers.IO) {
val results = HashMap<String, Any?>() // <1>
results["email"] = key // <2>

val database = databaseManager.inventoryDatabase
database?.let { db ->
val documentId = getCurrentUserDocumentId(key)
val doc = db.getDocument(documentId) // <3>
if (doc != null) {
results["givenName"] = doc.getString("givenName") // <4>
results["surname"] = doc.getString("surname") // <4>
results["jobTitle"] = doc.getString("jobTitle") // <4>
results["team"] = doc.getString("team") // <4>
results["imageData"] = doc.getBlob("imageData") // <4>
}
}
results // <5>
}
}

override suspend fun save(data: Map<String, Any?>): Boolean {
return withContext(Dispatchers.IO) {
val email = data["email"] as String
val documentId = getCurrentUserDocumentId(email)
val mutableDocument = MutableDocument(documentId, data)
try {
val database = databaseManager.inventoryDatabase
database?.save(mutableDocument)
} catch (e: CouchbaseLiteException) {
android.util.Log.e(e.message, e.stackTraceToString())
return@withContext false
}
return@withContext true
}
}

override suspend fun count(): Int {
return withContext(Dispatchers.IO) {
val database = databaseManager.inventoryDatabase
database?.let { db ->
val query = "SELECT COUNT(*) AS count FROM _ WHERE documentType='$userProfileType'"
val results = db.createQuery(query).execute().allResults()
return@withContext results[0].getInt("count")
}
return@withContext 0
}
}

override suspend fun delete(documentId: String): Boolean {
return withContext(Dispatchers.IO) {
var result = false
val database = databaseManager.inventoryDatabase
database?.let { db ->
val document = db.getDocument(documentId)
document?.let {
db.delete(it)
result = true
}
}
return@withContext result
}
}

private fun getCurrentUserDocumentId(currentUser: String): String {
return "user::${currentUser}"
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.couchbase.learningpath.services

import androidx.lifecycle.LiveData
import com.couchbase.learningpath.models.User

interface AuthenticationService {
val currentUser: LiveData<User?>
fun getCurrentUser() : User
fun authenticatedUser(username: String, password: String) : Boolean
suspend fun authenticatedUser(username: String, password: String) : Boolean
fun logout()
}
Original file line number Diff line number Diff line change
@@ -1,33 +1,48 @@
package com.couchbase.learningpath.services

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.couchbase.learningpath.data.DatabaseManager
import com.couchbase.learningpath.models.User
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class MockAuthenticationService : AuthenticationService {
class MockAuthenticationService(
private val databaseManager: DatabaseManager
) : AuthenticationService {

private var _user: User? = null
private var _mockUsers = HashMap<String, User>()
private val _user = MutableLiveData<User?>()
private val _mockUsers = HashMap<String, User>()

override val currentUser: LiveData<User?> = _user

override fun getCurrentUser(): User {
return _user?: User("", "", "")
return _user.value ?: User("", "", "")
}

override fun authenticatedUser(username: String, password: String): Boolean {
override suspend fun authenticatedUser(username: String, password: String): Boolean {
return if (_mockUsers.containsKey(username)){
val user = _mockUsers[username]
if (user?.password == password){
_user = user
withContext(Dispatchers.IO) {
//initialize database if needed
databaseManager.initializeDatabases(user)
withContext(Dispatchers.Main) {
_user.value = user
}
}
true
} else {
false
}
} else {
_user = User(username = username, password = password, team = "team2")
_user.value = User(username = username, password = password, team = "team2")
return true
}
}

override fun logout() {
_user = null
_user.value = null
}

init {
Expand Down
Loading