Skip to content
This repository has been archived by the owner on Feb 10, 2023. It is now read-only.

Feat/implmement friendship #12

Draft
wants to merge 12 commits into
base: develop
Choose a base branch
from
58 changes: 49 additions & 9 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension

plugins {
base
id("org.springframework.boot") version "2.3.4.RELEASE" apply false
id("org.springframework.boot") version "2.4.3" apply false
id("io.spring.dependency-management") version "1.0.10.RELEASE" apply false
kotlin("jvm") version "1.3.72" apply false
kotlin("plugin.spring") version "1.3.72" apply false
kotlin("plugin.jpa") version "1.3.72" apply false

kotlin("jvm") version "1.4.31" apply false
kotlin("plugin.spring") version "1.4.31" apply false
kotlin("plugin.jpa") version "1.4.31" apply false
}

allprojects {
Expand Down Expand Up @@ -42,6 +43,7 @@ subprojects {
configure<DependencyManagementExtension> {
imports {
mavenBom("org.keycloak.bom:keycloak-adapter-bom:11.0.3")
mavenBom("org.testcontainers:testcontainers-bom:1.15.3")
}
}

Expand All @@ -53,13 +55,22 @@ subprojects {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactive:1.4.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.4.3")
implementation("io.github.microutils:kotlin-logging:1.12.0")
testImplementation("org.spockframework:spock-core:2.0-M3-groovy-2.5")
testImplementation("org.spockframework:spock-bom:2.0-M3-groovy-2.5")
testImplementation("org.spockframework:spock-spring:2.0-M3-groovy-2.5")
testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
}
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("org.amshove.kluent:kluent:1.65")
testImplementation("org.testcontainers:testcontainers")
testImplementation("org.testcontainers:junit-jupiter")

}

repositories {
mavenCentral()
}

tasks.withType<Test> {
Expand All @@ -71,16 +82,45 @@ subprojects {
}

project(":ichi-server") {
val implementation by configurations
val api by configurations
dependencies {
implementation(project(":ichi-user"))
api(project(":ichi-common"))
api(project(":ichi-user"))
api(project(":ichi-friendship"))
}
}

project(":ichi-user") {
val api by configurations
val jar: Jar by tasks
val bootJar: BootJar by tasks

bootJar.enabled = false
jar.enabled = true

dependencies {
api(project(":ichi-common"))
}
}

project(":ichi-friendship") {
val api by configurations
val jar: Jar by tasks
val bootJar: BootJar by tasks

bootJar.enabled = false
jar.enabled = true

dependencies {
api(project(":ichi-common"))
api(project(":ichi-user"))
}
}

project(":ichi-common") {
val jar: Jar by tasks
val bootJar: BootJar by tasks

bootJar.enabled = false
jar.enabled = true
}
7 changes: 7 additions & 0 deletions ichi-common/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
plugins {
kotlin("jvm")
}

dependencies {
implementation(kotlin("stdlib"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.mrfourfour.ichi.common.domain

interface DomainEvent<I> {
val id: I
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.mrfourfour.ichi.common.domain

interface EventPublisher {
fun <T> publish(domainEvent: DomainEvent<T>)
}
11 changes: 11 additions & 0 deletions ichi-friendship/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
plugins {
groovy
kotlin("jvm")

}

dependencies {
implementation(kotlin("stdlib"))
implementation("com.arangodb:arangodb-java-driver:6.10.0")
implementation("com.arangodb:velocypack-module-jdk8:1.1.0")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.mrfourfour.ichi.friendship.application

import com.mrfourfour.ichi.friendship.domain.user.User
import com.mrfourfour.ichi.friendship.domain.user.UserRepository
import com.mrfourfour.ichi.user.domain.UserCreated
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.springframework.context.event.EventListener
import org.springframework.stereotype.Service

@Service
class UserCreatedEventHandler(
private val userRepository: UserRepository
) {

@EventListener
fun handle(userCreated: UserCreated) = GlobalScope.launch(Dispatchers.IO) {
val userId = userCreated.id.value
userRepository.save(User(userId))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.mrfourfour.ichi.friendship.domain.friendship

import com.mrfourfour.ichi.friendship.domain.user.User

class Friendship(
val from: User,
val to: User
) {
var id: Long = 0
internal set

/**
* When friendship create first, start from waited state.
*/
private val state: State = State.WAITED

val isFriend: Boolean
get() = true

private enum class State {
FRIEND,
WAITED
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.mrfourfour.ichi.friendship.domain.friendship

interface FriendshipRepository {
suspend fun findByUserId(id: String): List<Friendship>
suspend fun save(entity: Friendship): Friendship
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.mrfourfour.ichi.friendship.domain.user

import com.mrfourfour.ichi.friendship.domain.friendship.Friendship
import java.util.*

class User(
val id: String
) {

private val friends: MutableList<Friendship> = mutableListOf()

private val friendee: MutableList<Friendship> = mutableListOf()

fun requestFriends(peer: User) {
val friendship = Friendship(this, peer)
friends.add(friendship)
}

fun acceptFriendsaddFollower(user: User) {
}

override fun equals(other: Any?): Boolean {
if (other == null) return false
if (other !is User) return false

return this.id == other.id
}

override fun hashCode(): Int = Objects.hashCode(id)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.mrfourfour.ichi.friendship.domain.user

interface UserRepository {
suspend fun save(user: User): User

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.mrfourfour.ichi.friendship.infrastructure

import com.arangodb.async.ArangoDBAsync
import com.arangodb.async.ArangoEdgeCollectionAsync
import com.arangodb.async.ArangoVertexCollectionAsync
import com.arangodb.entity.EdgeEntity
import com.mrfourfour.ichi.friendship.domain.friendship.Friendship
import com.mrfourfour.ichi.friendship.domain.friendship.FriendshipRepository
import com.mrfourfour.ichi.friendship.domain.user.User
import com.mrfourfour.ichi.friendship.infrastructure.config.database
import kotlinx.coroutines.future.await
import mu.KLogging
import org.springframework.stereotype.Repository

@Repository
class ArangoFriendshipRepository(
private val arango: ArangoDBAsync
) : FriendshipRepository {

private val edges: ArangoEdgeCollectionAsync
private val vertexes: ArangoVertexCollectionAsync

init {
val db = arango.db(database)
val graph = db.graph("friendship")
edges = graph.edgeCollection("is_friend_with")
vertexes = graph.vertexCollection("user")
}

override suspend fun findByUserId(id: String): List<Friendship> {
val result = vertexes.getVertex<User>(id).await()
println(result)
return listOf()
}

override suspend fun save(friendship: Friendship): Friendship {

val inserted = edges
.insertEdge(friendship.dataEntity)
.await()
println(inserted)
return friendship
}

private val Friendship.dataEntity get() = FriendshipEntity(
"user/${this.from.id}",
"user/${this.to.id}"
)

private val EdgeEntity.friendship get() = Friendship(
User("hi"), User("hello")
)

companion object: KLogging()
}
private inline fun <reified T> ArangoVertexCollectionAsync.getVertex(id: String) = getVertex(id, T::class.java)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.mrfourfour.ichi.friendship.infrastructure

import com.arangodb.entity.DocumentField
import com.arangodb.entity.EdgeEntity

class FriendshipEntity(
@DocumentField(DocumentField.Type.FROM)
val from: String,
@DocumentField(DocumentField.Type.TO)
val to: String,
): EdgeEntity() {
val type = TYPE

companion object {
private const val TYPE = "is_friend_with"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.mrfourfour.ichi.friendship.infrastructure

import com.arangodb.entity.VertexEntity

class UserEntity : VertexEntity() {
companion object {
const val TYPE = "user"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.mrfourfour.ichi.friendship.infrastructure.config

import com.arangodb.async.ArangoDBAsync
import com.arangodb.async.ArangoGraphAsync
import kotlinx.coroutines.runBlocking
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class ArangoConfig {

@Bean
fun arango(properties: ArangoProperties): ArangoDBAsync =
ArangoDBAsync.Builder()
.host(properties.host, properties.port)
.timeout(properties.timeout)
.user(properties.user)
.password(properties.password)
.useSsl(properties.useSsl)
.build()

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.mrfourfour.ichi.friendship.infrastructure.config

import com.arangodb.async.ArangoDBAsync
import com.arangodb.entity.EdgeDefinition
import com.arangodb.model.CollectionCreateOptions
import kotlinx.coroutines.future.await
import kotlinx.coroutines.runBlocking
import mu.KLogging
import org.springframework.context.annotation.Profile
import org.springframework.stereotype.Component

@Component
@Profile("default")
class ArangoMigration(
val arango: ArangoDBAsync
) {
init {
runBlocking { migrate() }
}

suspend fun migrate() {
val exists = arango.db(database).exists().await()

if (exists) {
logger.info("database already exists. ignore migration.")
return
}

logger.info("database doesn't exist. start to migrate")
doMigrate()
}

private suspend fun doMigrate() {
arango.createDatabase(database).await()

val db = arango.db(database)
db.createGraph("friendship", createEdgeDefinition()).await()
}

private fun createEdgeDefinition(): List<EdgeDefinition> {
return listOf(
EdgeDefinition()
.collection("is_friend_with")
.from("user")
.to("user")
)
}

companion object : KLogging()
}
Loading