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

refactor(common, news): unified data models, entity events and auto JSON serialization #58

Merged
Show file tree
Hide file tree
Changes from 7 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
14 changes: 10 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
buildscript {
ext.kotlin_version = '1.3.61'

repositories {
mavenCentral()
google()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
}
}

plugins {
id 'org.jetbrains.kotlin.jvm' version'1.3.50'
}
apply plugin: 'kotlin'

subprojects {
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.50")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
classpath("org.jetbrains.kotlin:kotlin-serialization:$kotlin_version")
classpath("com.github.jengelman.gradle.plugins:shadow:5.1.0")
}
}
Expand Down
14 changes: 12 additions & 2 deletions common/build.gradle
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
plugins {
id "idea"
id "kotlin"
id "application"
}
apply plugin: 'kotlinx-serialization'

dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib:1.3.50")
api("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version")
api("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version")
api("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0")

// API
api("com.google.protobuf:protobuf-java:3.6.1")
api("io.grpc:grpc-stub:1.18.0")
api("de.hpi.cloud:hpi-cloud:0.0.11")
api("de.hpi.cloud:hpi-cloud:0.0.12")

// Storage
api("com.couchbase.client:java-client:2.7.9")
}

compileKotlin {
kotlinOptions {
freeCompilerArgs += "-Xuse-experimental=kotlin.Experimental"
}
}
9 changes: 9 additions & 0 deletions common/src/main/kotlin/de/hpi/cloud/common/Context.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package de.hpi.cloud.common

import de.hpi.cloud.common.entity.Id
import java.util.*

data class Context(
val author: Id<Party>,
val languageRanges: List<Locale.LanguageRange>
)
17 changes: 0 additions & 17 deletions common/src/main/kotlin/de/hpi/cloud/common/Entity.kt

This file was deleted.

11 changes: 11 additions & 0 deletions common/src/main/kotlin/de/hpi/cloud/common/Party.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package de.hpi.cloud.common

import de.hpi.cloud.common.entity.Entity
import kotlinx.serialization.Serializable

@Serializable
data class Party(
val name: String
) : Entity<Party>() {
companion object : Entity.Companion<Party>("party")
}
10 changes: 10 additions & 0 deletions common/src/main/kotlin/de/hpi/cloud/common/Persistable.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package de.hpi.cloud.common

import com.google.protobuf.GeneratedMessageV3

abstract class Persistable<P : Persistable<P>> {
interface ProtoSerializer<P : Persistable<P>, Proto : GeneratedMessageV3> {
fun fromProto(proto: Proto, context: Context): P
fun toProto(persistable: P, context: Context): Proto
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package de.hpi.cloud.common
import com.couchbase.client.java.Bucket
import com.couchbase.client.java.CouchbaseCluster
import com.google.protobuf.GeneratedMessageV3
import de.hpi.cloud.common.utils.couchbase.openCouchbase
import de.hpi.cloud.common.couchbase.openCouchbase
import de.hpi.cloud.common.entity.Id
import de.hpi.cloud.common.grpc.preferredLocales
import de.hpi.cloud.common.utils.removeFirst
import io.grpc.*

Expand All @@ -17,9 +19,17 @@ class Service<S : BindableService>(
const val PORT_DEFAULT = 50051
const val PORT_VARIABLE = "HPI_CLOUD_PORT"

private val requestMetadata = mutableListOf<RequestWithMetadata<GeneratedMessageV3>>()
fun metadataForRequest(request: GeneratedMessageV3): Metadata? =
requestMetadata.firstOrNull { it.request === request }?.metadata
private val requestMetadata = mutableListOf<RequestWithMetadata>()
fun contextForRequest(request: Any): Context? {
return requestMetadata.firstOrNull { it.request === request }
?.metadata
?.let {
Context(
author = Id("0"),
languageRanges = it.preferredLocales
)
}
}
}

private val server: Server
Expand Down Expand Up @@ -55,7 +65,12 @@ class Service<S : BindableService>(
require(message is GeneratedMessageV3)

request = message
requestMetadata.add(RequestWithMetadata(message, headers))
requestMetadata.add(
RequestWithMetadata(
message,
headers
)
)
super.onMessage(message)
}

Expand Down Expand Up @@ -86,7 +101,7 @@ class Service<S : BindableService>(
}

fun stop() {
if (isStopped) throw IllegalStateException("$name is already stopped")
check(!isStopped) { "$name is already stopped" }
isStopped = true

println("Stopping $name")
Expand All @@ -105,8 +120,8 @@ class Service<S : BindableService>(
server.awaitTermination()
}

data class RequestWithMetadata<ReqT : GeneratedMessageV3>(
val request: ReqT,
data class RequestWithMetadata(
val request: Any,
val metadata: Metadata?
)
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package de.hpi.cloud.common.utils.couchbase
package de.hpi.cloud.common.couchbase

const val KEY_TYPE = "type"
const val KEY_VERSION = "version"
const val KEY_ID = "id"
const val KEY_METADATA = "meta"
const val KEY_METADATA_CREATED_AT = "createdAt"
const val KEY_VALUE = "value"

const val NESTED_SEPARATOR = "."

fun devDesignDoc(designDoc: String) = "dev_$designDoc"
const val VIEW_BY_ID = "byId"
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package de.hpi.cloud.common.utils.couchbase
package de.hpi.cloud.common.couchbase

import com.couchbase.client.java.Bucket
import com.couchbase.client.java.CouchbaseAsyncCluster
import com.couchbase.client.java.CouchbaseCluster
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment
import com.couchbase.client.java.view.ViewQuery
import com.couchbase.client.java.view.ViewResult

const val COUCHBASE_CONNECT_TIMEOUT = 15000L
const val COUCHBASE_NODES_VARIABLE = "HPI_CLOUD_COUCHBASE_NODES"
Expand Down Expand Up @@ -36,5 +34,3 @@ fun withBucket(bucket: String, nodesOverride: List<String> = emptyList(), runnab
.close()
}.disconnect()
}

fun ViewQuery.execute(bucket: Bucket): ViewResult = bucket.query(this)
31 changes: 31 additions & 0 deletions common/src/main/kotlin/de/hpi/cloud/common/couchbase/Document.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package de.hpi.cloud.common.couchbase

import com.couchbase.client.java.AsyncBucket
import com.couchbase.client.java.Bucket
import com.couchbase.client.java.document.RawJsonDocument
import de.hpi.cloud.common.entity.Entity
import de.hpi.cloud.common.entity.Id
import de.hpi.cloud.common.entity.Wrapper
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
import rx.Observable

val jsonConfiguration = JsonConfiguration.Stable.copy(
strictMode = true,
allowStructuredMapKeys = false
)
val json = Json(jsonConfiguration)

inline fun <reified E : Entity<E>> RawJsonDocument.parseWrapper(): Wrapper<E> {
return json.parse(Wrapper.jsonSerializerFor<E>(), content())
}

inline fun <reified E : Entity<E>> Bucket.get(id: Id<E>): Wrapper<E>? {
return get(id.documentId<E>(), RawJsonDocument::class.java)?.parseWrapper()
?: return null
}

inline fun <reified E : Entity<E>> AsyncBucket.get(id: Id<E>): Observable<Wrapper<E>> {
return get(id.documentId<E>(), RawJsonDocument::class.java)
.map { it.parseWrapper<E>() }
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package de.hpi.cloud.common.utils.couchbase
package de.hpi.cloud.common.couchbase

import com.couchbase.client.java.query.dsl.Expression
import com.couchbase.client.java.query.dsl.Expression.*
import com.couchbase.client.java.query.dsl.Sort
import com.couchbase.client.java.query.dsl.Sort.asc
import com.couchbase.client.java.query.dsl.Sort.desc
import de.hpi.cloud.common.utils.protobuf.TIMESTAMP_MILLIS
import de.hpi.cloud.common.utils.protobuf.TIMESTAMP_NANOS
import de.hpi.cloud.common.types.LocalDateTime

fun and(vararg expressions: Expression?): Expression {
return expressions.filterNotNull().run {
Expand All @@ -16,18 +15,24 @@ fun and(vararg expressions: Expression?): Expression {
}

fun ascTimestamp(field: Expression): Array<Sort> {
return arrayOf(asc("$field.$TIMESTAMP_MILLIS"), asc("$field.$TIMESTAMP_NANOS"))
return arrayOf(
asc("$field.${LocalDateTime.JsonSerializer.KEY_MILLIS}"),
asc("$field.${LocalDateTime.JsonSerializer.KEY_NANOS}")
)
}

fun descTimestamp(field: Expression): Array<Sort> {
return arrayOf(desc("$field.$TIMESTAMP_MILLIS"), desc("$field.$TIMESTAMP_NANOS"))
return arrayOf(
desc("$field.${LocalDateTime.JsonSerializer.KEY_MILLIS}"),
desc("$field.${LocalDateTime.JsonSerializer.KEY_NANOS}")
)
}

/**
* Builds an expression for a *nested* field with proper escaping.
*/
fun n(vararg part: String): Expression {
return x(part.joinToString(NESTED_SEPARATOR.toString()) { i(it).toString() })
return x(part.joinToString(NESTED_SEPARATOR) { i(it).toString() })
}

/**
Expand Down
Loading