Skip to content

Commit

Permalink
feature: Project setup on backend
Browse files Browse the repository at this point in the history
  • Loading branch information
LichtHund committed Oct 6, 2023
1 parent bc8fd8b commit a180a17
Show file tree
Hide file tree
Showing 14 changed files with 110 additions and 69 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ dependencies {
implementation(libs.bundles.logger)

implementation(libs.bundles.database)
implementation("io.ktor:ktor-client-logging-jvm:2.3.4")
}
12 changes: 10 additions & 2 deletions app/src/main/kotlin/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
*/
package dev.triumphteam.docsly

import com.zaxxer.hikari.HikariDataSource
import dev.triumphteam.docsly.config.createOrGetConfig
import dev.triumphteam.docsly.controller.apiGuild
import dev.triumphteam.docsly.database.entity.DocsTable
import dev.triumphteam.docsly.defaults.Defaults
import dev.triumphteam.docsly.meilisearch.Meili
import dev.triumphteam.docsly.project.Projects
Expand All @@ -39,6 +41,9 @@ import io.ktor.server.request.path
import io.ktor.server.resources.Resources
import io.ktor.server.routing.routing
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.transactions.transaction
import org.slf4j.event.Level

private val config = createOrGetConfig()
Expand All @@ -48,7 +53,11 @@ public fun main() {
}

public fun Application.module() {
// Database.connect(HikariDataSource(config.postgres.toHikariConfig()))
Database.connect(HikariDataSource(config.postgres.toHikariConfig()))

transaction {
SchemaUtils.create(DocsTable)
}

/*install(CORS) {
anyHost()
Expand All @@ -71,7 +80,6 @@ public fun Application.module() {
install(Projects)

routing {

// Setup guild api/routing
apiGuild()

Expand Down
1 change: 0 additions & 1 deletion app/src/main/kotlin/controller/ApiGuildController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import io.ktor.server.response.respond
import io.ktor.server.routing.Routing

public fun Routing.apiGuild() {

val defaults = plugin(Defaults)
val projects = plugin(Projects)

Expand Down
19 changes: 9 additions & 10 deletions app/src/main/kotlin/database/entity/DocsEntity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,22 @@ import dev.triumphteam.docsly.elements.DocElement
import org.jetbrains.exposed.dao.LongEntity
import org.jetbrains.exposed.dao.LongEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IdTable
import org.jetbrains.exposed.dao.id.LongIdTable
import org.jetbrains.exposed.sql.Column

public object DocsTable : IdTable<Long>() {
public val guild: Column<Long> = long("guild_id").uniqueIndex()
public val project: Column<String> = text("defaults").uniqueIndex()
public val version: Column<String> = text("version").uniqueIndex()
public val location: Column<String> = text("location").uniqueIndex()
public val doc: Column<DocElement> = serializable("doc")
public object DocsTable : LongIdTable() {

override val primaryKey: PrimaryKey = PrimaryKey(guild, version, location)
override val id: Column<EntityID<Long>> = guild.entityId()
public val guild: Column<String> = text("guild_id")
public val project: Column<String> = text("project")
public val version: Column<String> = text("version")
public val doc: Column<DocElement> = serializable("doc")
}

public class DocEntity(entityId: EntityID<Long>) : LongEntity(entityId) {
public companion object : LongEntityClass<DocEntity>(DocsTable)

public var guild: String by DocsTable.guild
public var project: String by DocsTable.project
public var version: String by DocsTable.version
public var guild: Long by DocsTable.guild
public var doc: DocElement by DocsTable.doc
}
20 changes: 9 additions & 11 deletions app/src/main/kotlin/database/exposed/SerializableColumn.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,21 @@
package dev.triumphteam.docsly.database.exposed

import com.impossibl.postgres.jdbc.PGArray
import dev.triumphteam.docsly.project.Projects
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.serializer
import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.TextColumnType
import java.sql.Clob
import kotlin.reflect.KClass

public val json: Json = Json

public inline fun <reified T : Any> Table.serializable(name: String, serializer: KSerializer<T>): Column<T> =
registerColumn(name, SerializableColumnType(T::class, serializer))

public inline fun <reified T : Any> Table.serializable(name: String): Column<T> =
registerColumn(name, SerializableColumnType(T::class, json.serializersModule.serializer()))
registerColumn(name, SerializableColumnType(T::class, Projects.JSON.serializersModule.serializer()))

@Suppress("UNCHECKED_CAST")
public class SerializableColumnType<T : Any>(
Expand All @@ -50,16 +48,16 @@ public class SerializableColumnType<T : Any>(

/** When writing the value, it can either be a full on list, or individual values. */
override fun notNullValueToDB(value: Any): Any = when {
klass.isInstance(value) -> json.encodeToString(serializer, value as T)
klass.isInstance(value) -> Projects.JSON.encodeToString(serializer, value as T)
else -> error("$value of ${value::class.qualifiedName} is not an instance of ${klass.simpleName}")
}

/** When getting the value it can be more than just [PGArray]. */
override fun valueFromDB(value: Any): Any = when (value) {
is Clob -> json.decodeFromString(serializer, value.characterStream.readText())
is ByteArray -> json.decodeFromString(serializer, String(value))
is String -> json.decodeFromString(serializer, value)
else -> error("$value of ${value::class.qualifiedName} could not be decoded into ${klass.simpleName}")
is Clob -> Projects.JSON.decodeFromString(serializer, value.characterStream.readText())
is ByteArray -> Projects.JSON.decodeFromString(serializer, String(value))
is String -> Projects.JSON.decodeFromString(serializer, value)
else -> value
}
}

Expand All @@ -74,13 +72,13 @@ public class SerializableListColumnType<T : Any>(

/** When writing the value, it can either be a full on list, or individual values. */
override fun notNullValueToDB(value: Any): Any = when (value) {
is List<*> -> json.encodeToString(serializer, value as List<T>)
is List<*> -> Projects.JSON.encodeToString(serializer, value as List<T>)
else -> error("$value of ${value::class.qualifiedName} is not an instance of ${klass.simpleName}")
}

/** When getting the value it can be more than just [PGArray]. */
override fun valueFromDB(value: Any): Any = when (value) {
is String -> json.decodeFromString(serializer, value)
is String -> Projects.JSON.decodeFromString(serializer, value)
else -> {
println("Oh boy! ${value.javaClass}")
}
Expand Down
13 changes: 11 additions & 2 deletions app/src/main/kotlin/meilisearch/MeiliClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.auth.Auth
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.logging.DEFAULT
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logger
import io.ktor.client.plugins.logging.Logging
import io.ktor.client.plugins.resources.Resources
import io.ktor.client.plugins.resources.delete
import io.ktor.client.plugins.resources.post
Expand All @@ -56,6 +60,10 @@ public class MeiliClient(
install(Resources)
install(Auth) { api(apiKey) } // Auto setup authentication
install(ContentNegotiation) { json() } // Using Kotlin serialization for content negotiation
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.BODY
}

defaultRequest {
this.host = host
Expand Down Expand Up @@ -114,10 +122,11 @@ public class MeiliClient(

return client.post(Indexes.Uid.Documents(Indexes.Uid(uid = uid))) {
contentType(ContentType.Application.Json)
parameter(PRIMARY_KEY_PARAM, pk)
setBody(documents)
pk?.let { parameter(PRIMARY_KEY_PARAM, it) }
setBody<List<T>>(documents)
}.also {
println(it)
println(it.body<String>())
}
}

Expand Down
70 changes: 47 additions & 23 deletions app/src/main/kotlin/project/Projects.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
package dev.triumphteam.docsly.project

import dev.triumphteam.docsly.database.entity.DocEntity
import dev.triumphteam.docsly.defaults.Defaults
import dev.triumphteam.docsly.elements.DocElement
import dev.triumphteam.docsly.meilisearch.Meili
import dev.triumphteam.docsly.meilisearch.annotation.PrimaryKey
import io.ktor.server.application.Application
import io.ktor.server.application.BaseApplicationPlugin
import io.ktor.server.application.plugin
import io.ktor.util.AttributeKey
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.jetbrains.exposed.sql.transactions.transaction

/**
* A class representing a Projects plugin.
* Used for setting up projects, deleting, etc.
*
* @property meili The Meili plugin instance.
* @property defaults The Defaults plugin instance.
*/
public class Projects(private val meili: Meili, private val defaults: Defaults) {

public companion object Plugin : BaseApplicationPlugin<Application, Projects, Projects> {
Expand All @@ -23,43 +31,59 @@ public class Projects(private val meili: Meili, private val defaults: Defaults)
}

public fun indexKeyFor(guild: String, project: String, version: String): String {
return "$guild:$project:$version"
return "${guild}_${project}_$version"
}
}

private val json = Json {
explicitNulls = false
ignoreUnknownKeys = true
public val JSON: Json = Json {
explicitNulls = false
ignoreUnknownKeys = true
}
}

/**
* Sets up projects for a specified guild with their projects.
*
* @param guild The name of the guild.
* @param projects A map containing project names as keys and sets of versions as values.
*/
public suspend fun setupProjects(guild: String, projects: Map<String, Set<String>>) {
// transaction {
val mapped = projects.mapValues { (project, versions) ->
versions.associateWith { version ->
val jsonFile = defaults.resolve(project, version.replace(".", "_"))
json.decodeFromString<List<DocElement>>(jsonFile.readText())
val mapped = transaction {
projects.mapValues { (project, versions) ->
versions.associateWith { version ->
val jsonFile = defaults.resolve(project, version.replace(".", "_"))
val docs = JSON.decodeFromString<List<DocElement>>(jsonFile.readText())

docs.map { doc ->
DocEntity.new {
this.guild = guild
this.project = project
this.version = version
this.doc = doc
}
}
}
}
}

runBlocking {
mapped.forEach { (project, versions) ->
versions.forEach { (version, docs) ->
meili.client.index(indexKeyFor(guild, project, version)).addDocuments(
docs.map { doc ->
IndexDocument(doc.location, doc.createReferences())
}
mapped.forEach { (project, versions) ->
versions.forEach { (version, docs) ->
docs.chunked(1000).forEach { chunk ->
meili.client.index(
indexKeyFor(guild, project, version.replace(".", "_")),
primaryKey = "id",
).addDocuments(
chunk.map { doc ->
IndexDocument(doc.id.value, doc.doc.createReferences())
}.also { println(JSON.encodeToString(it)) },
)
}
}
}

// TODO: Postgres
// }
}
}

@Serializable
public data class IndexDocument(
@PrimaryKey public val location: String,
public val id: Long,
public val references: List<String>,
)
22 changes: 11 additions & 11 deletions dokka-plugin/src/main/kotlin/renderer/JsonRenderer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,6 @@
package dev.triumphteam.docsly.renderer

import dev.triumphteam.docsly.DocslyDokkaPlugin
import dev.triumphteam.docsly.renderer.ext.description
import dev.triumphteam.docsly.renderer.ext.extraModifiers
import dev.triumphteam.docsly.renderer.ext.finalVisibility
import dev.triumphteam.docsly.renderer.ext.getDocumentation
import dev.triumphteam.docsly.renderer.ext.language
import dev.triumphteam.docsly.renderer.ext.returnType
import dev.triumphteam.docsly.renderer.ext.serialGenerics
import dev.triumphteam.docsly.renderer.ext.toPath
import dev.triumphteam.docsly.renderer.ext.toSerialAnnotations
import dev.triumphteam.docsly.renderer.ext.toSerialModifiers
import dev.triumphteam.docsly.renderer.ext.toSerialType
import dev.triumphteam.docsly.elements.ClassKind
import dev.triumphteam.docsly.elements.ClassLike
import dev.triumphteam.docsly.elements.Language
Expand All @@ -51,6 +40,17 @@ import dev.triumphteam.docsly.elements.SerializableParameter
import dev.triumphteam.docsly.elements.SerializableProperty
import dev.triumphteam.docsly.elements.SerializableTypeAlias
import dev.triumphteam.docsly.elements.SuperType
import dev.triumphteam.docsly.renderer.ext.description
import dev.triumphteam.docsly.renderer.ext.extraModifiers
import dev.triumphteam.docsly.renderer.ext.finalVisibility
import dev.triumphteam.docsly.renderer.ext.getDocumentation
import dev.triumphteam.docsly.renderer.ext.language
import dev.triumphteam.docsly.renderer.ext.returnType
import dev.triumphteam.docsly.renderer.ext.serialGenerics
import dev.triumphteam.docsly.renderer.ext.toPath
import dev.triumphteam.docsly.renderer.ext.toSerialAnnotations
import dev.triumphteam.docsly.renderer.ext.toSerialModifiers
import dev.triumphteam.docsly.renderer.ext.toSerialType
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
Expand Down
4 changes: 2 additions & 2 deletions gradle-plugin/src/main/kotlin/DocslyGradlePlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import org.jetbrains.dokka.gradle.DokkaTask
import org.jetbrains.dokka.gradle.DokkaTaskPartial

private const val DOKKA_ID = "org.jetbrains.dokka"
private const val DOCLOPEDIA_DOKKA = "dev.triumphteam:docsly-dokka-plugin:0.0.2"
private const val DOCSLY_DOKKA_PLUGIN_VERSION = "dev.triumphteam:docsly-dokka-plugin:0.0.3"

public open class DocslyGradlePlugin : Plugin<Project> {

Expand All @@ -44,7 +44,7 @@ public open class DocslyGradlePlugin : Plugin<Project> {
}

setupDokkaTask("dokkaDocsly") {
plugins.dependencies.add(project.dependencies.create(DOCLOPEDIA_DOKKA))
plugins.dependencies.add(project.dependencies.create(DOCSLY_DOKKA_PLUGIN_VERSION))
description = "Generates JSON documentation to be used by Docsly."
}
}
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
group=dev.triumphteam
version=0.0.2
version=0.0.4

org.gradle.jvmargs=-Xmx4G
4 changes: 3 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ ktor-client-auth = { module = "io.ktor:ktor-client-auth", version.ref = "ktor" }
ktor-client-negociation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
ktor-client-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
ktor-client-resources = { module = "io.ktor:ktor-client-resources", version.ref = "ktor" }
ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }

# DB
exposed = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "exposed" }
Expand Down Expand Up @@ -89,7 +90,8 @@ ktor-client = [
"ktor-client-auth",
"ktor-client-negociation",
"ktor-client-json",
"ktor-client-resources"
"ktor-client-resources",
"ktor-client-logging",
]
logger = [
"logger-api",
Expand Down
2 changes: 1 addition & 1 deletion serializable/src/main/kotlin/elements/ClassLike.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ public sealed interface ClassLike :

override fun createReferences(): List<String> {
return listOf(
"${path.packagePath}.$name",
name,
"${path.packagePath}.$name"
)
}
}
Expand Down
Loading

0 comments on commit a180a17

Please sign in to comment.