Skip to content

Support simultanious input + output function for model #14

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 2 commits into
base: main
Choose a base branch
from
Open
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
31 changes: 24 additions & 7 deletions adapters/graphql/src/main/kotlin/graphql/Usecases.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,19 @@ fun SchemaBuilder.usecases(usecases: Collection<UsecaseType<*>>) {
.map { it.kClass }
.toSet()

val types = usecases.filter { it::class.hasAnnotation<Query>() || it::class.hasAnnotation<Mutation>() }.flatMap {
val input = mutableSetOf<KClass<*>>()
val output = mutableSetOf<KClass<*>>()

usecases.filter { it::class.hasAnnotation<Query>() || it::class.hasAnnotation<Mutation>() }.map {
usecase(it)
types(it)
}.toSet()
input.addAll(inputTypes(it))
output.addAll(outputTypes(it))
}

types(types, scalars).forEach { type(it) }
val allInput = types(input, scalars)
val allOutput = types(output, scalars)

(allInput + allOutput).forEach { type(it, allInput.contains(it), allOutput.contains(it)) }
}

fun SchemaBuilder.usecase(usecase: UsecaseType<*>) {
Expand Down Expand Up @@ -83,7 +90,8 @@ fun <R, A0, U : UsecaseA1<A0, R>> AbstractOperationDSL.usecase(usecase: U): Reso
}
}

fun types(usecase: UsecaseType<*>) = (usecase.args + usecase.result).flatMap { types(it) }
fun inputTypes(usecase: UsecaseType<*>) = usecase.args.flatMap { types(it) }
fun outputTypes(usecase: UsecaseType<*>) = types(usecase.result)

fun types(type: KType): List<KClass<*>> = type.arguments.mapNotNull { it.type?.let { types(it) } }.flatten() +
if (type.isCollection()) {
Expand All @@ -103,11 +111,20 @@ fun types(types: Set<KClass<*>>, ignore: Set<KClass<*>>): Set<KClass<*>> {
}

@Suppress("UNCHECKED_CAST")
fun <T : Any> SchemaBuilder.type(type: KClass<T>) {
fun <T : Any> SchemaBuilder.type(type: KClass<T>, input: Boolean, output: Boolean) {
when {
type.java.isEnum -> enum(type as KClass<out Enum<*>>)
type.isValue || type.allSuperclasses.any { it == ValueClass::class } -> valueClassScalar(type)
else -> type(type) {}
else -> {
if (input) {
type(type) {
this.name = type.simpleName + "Input"
}
}
if (output) {
type(type) {}
}
}
}
}

Expand Down
7 changes: 0 additions & 7 deletions use-cases/src/main/kotlin/usecases/model/User.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,7 @@ data class UserModel(
val locked: Boolean
) {
constructor(user: User) : this(user.id, user.email, user.authorities, user.locked)
}

data class UpdateUserModel(
val id: Int,
val email: Email,
val authorities: List<Authorities>,
val locked: Boolean
) {
fun toUser(hash: PasswordHash) =
User(id = id, email = email, authorities = authorities, password = hash, locked = locked)
}
Expand Down
5 changes: 2 additions & 3 deletions use-cases/src/main/kotlin/usecases/usecase/user/UpdateUser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import domain.EmailAlreadyExistsException
import domain.UserNotFoundException
import domain.entity.user.Authorities
import domain.repository.UserRepository
import usecases.model.UpdateUserModel
import usecases.model.UserModel
import usecases.usecase.Mutation
import usecases.usecase.UsecaseA1
Expand All @@ -14,10 +13,10 @@ import kotlin.reflect.typeOf
class UpdateUser(
private val repository: UserRepository,
private val userExists: UserExists,
) : UsecaseA1<UpdateUserModel, UserModel>(typeOf<UpdateUserModel>(), typeOf<UserModel>()) {
) : UsecaseA1<UserModel, UserModel>(typeOf<UserModel>(), typeOf<UserModel>()) {

override val authorities = listOf(Authorities.USER)
override suspend fun executor(authentication: UserModel?, a0: UpdateUserModel): UserModel {
override suspend fun executor(authentication: UserModel?, a0: UserModel): UserModel {
val old = repository.findById(a0.id) ?: throw UserNotFoundException()
if (old.email != a0.email && userExists(authentication, a0.email)) throw EmailAlreadyExistsException()
return UserModel(repository.update(a0.toUser(old.password)))
Expand Down
17 changes: 7 additions & 10 deletions use-cases/src/test/kotlin/usecases/usecase/user/UpdateUserTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import domain.repository.UserRepository
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.runBlocking
import usecases.model.UpdateUserModel
import usecases.model.UserModel
import usecases.usecase.UsecaseTests
import kotlin.test.Test
Expand All @@ -21,16 +20,14 @@ class UpdateUserTests : UsecaseTests {
val userExists = UserExists(repository)
override val usecase = UpdateUser(repository, userExists)

val updateUserModel = UpdateUserModel(user.id, user.email, user.authorities, user.locked)
val userModel =
UserModel(updateUserModel.id, updateUserModel.email, updateUserModel.authorities, updateUserModel.locked)
val userModel = UserModel(user.id, user.email, user.authorities, user.locked)

@Test
override fun success() {
runBlocking {
every { runBlocking { repository.findById(updateUserModel.id) } } returns user
every { runBlocking { repository.findById(userModel.id) } } returns user
every { runBlocking { repository.update(any()) } } returns user
val result = usecase(userModel, updateUserModel)
val result = usecase(userModel, userModel)

assertEquals(userModel, result)
}
Expand All @@ -40,7 +37,7 @@ class UpdateUserTests : UsecaseTests {
override fun unauthenticated() {
runBlocking {
assertFailsWith<LoginException> {
usecase(null, updateUserModel)
usecase(null, userModel)
}
}
}
Expand All @@ -49,7 +46,7 @@ class UpdateUserTests : UsecaseTests {
override fun `No user roles`() {
runBlocking {
assertFailsWith<AuthorizationException> {
usecase(userModel.copy(authorities = emptyList()), updateUserModel)
usecase(userModel.copy(authorities = emptyList()), userModel)
}
}
}
Expand All @@ -59,10 +56,10 @@ class UpdateUserTests : UsecaseTests {
runBlocking {
assertFailsWith<EmailAlreadyExistsException> {
val newEmail = Email("calvin@cargoledger.nl")
every { runBlocking { repository.findById(updateUserModel.id) } } returns user
every { runBlocking { repository.findById(userModel.id) } } returns user
every { runBlocking { repository.findByEmail(newEmail) } } returns user.copy(email = newEmail)
every { runBlocking { repository.update(any()) } } returns user
val result = usecase(userModel, updateUserModel.copy(email = newEmail))
val result = usecase(userModel, userModel.copy(email = newEmail))

assertEquals(result, userModel.copy(email = newEmail))
}
Expand Down