-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: Barebones bot and document rendering
- Loading branch information
Showing
26 changed files
with
885 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,59 @@ | ||
package dev.triumphteam.docsly.controller | ||
|
||
import dev.triumphteam.docsly.api.GuildSetupRequest | ||
import dev.triumphteam.docsly.database.entity.DocumentEntity | ||
import dev.triumphteam.docsly.defaults.Defaults | ||
import dev.triumphteam.docsly.elements.DocElement | ||
import dev.triumphteam.docsly.meilisearch.Meili | ||
import dev.triumphteam.docsly.project.DocumentSearchResult | ||
import dev.triumphteam.docsly.project.IndexDocument | ||
import dev.triumphteam.docsly.project.Projects | ||
import dev.triumphteam.docsly.resource.GuildApi | ||
import io.ktor.http.HttpStatusCode | ||
import io.ktor.server.application.call | ||
import io.ktor.server.application.plugin | ||
import io.ktor.server.request.receive | ||
import io.ktor.server.resources.get | ||
import io.ktor.server.resources.post | ||
import io.ktor.server.response.respond | ||
import io.ktor.server.routing.Routing | ||
import org.jetbrains.exposed.sql.transactions.transaction | ||
|
||
public fun Routing.apiGuild() { | ||
val defaults = plugin(Defaults) | ||
val projects = plugin(Projects) | ||
val meili = plugin(Meili) | ||
|
||
post<GuildApi.Guild.Setup> { setup -> | ||
val guild = setup.parent.guild | ||
val setupDefaults = call.receive<GuildSetupRequest>().defaults | ||
|
||
val defaultProjects = defaults.defaultProjects() | ||
|
||
// Validate data | ||
setupDefaults.forEach { (project, versions) -> | ||
val defaultVersions = defaultProjects[project] ?: run { | ||
call.respond(HttpStatusCode.BadRequest, "Invalid default project '$project'.") | ||
return@post | ||
} | ||
|
||
versions.forEach { version -> | ||
// TODO: Figure better way to get version, and not use folder name | ||
val replaced = version.replace(".", "_") | ||
if (replaced !in defaultVersions) { | ||
call.respond(HttpStatusCode.BadRequest, "Invalid default project version '$version' for project '$project'.") | ||
return@post | ||
} | ||
} | ||
} | ||
|
||
post<GuildApi.Guild.Setup> { api -> | ||
// If it goes well nothing will throw and it'll work well! | ||
projects.setupProjects(guild, setupDefaults) | ||
projects.setup(api.parent.guild, defaults) | ||
|
||
// So we return "accepted" | ||
call.respond(HttpStatusCode.Accepted) | ||
} | ||
|
||
get<GuildApi.Guild.Projects> { api -> | ||
call.respond(projects.getProjects(api.parent.guild)) | ||
} | ||
|
||
get<GuildApi.Guild.Search> { api -> | ||
val project = projects.getProject(api.parent.guild, api.project, api.version) ?: run { | ||
call.respond(HttpStatusCode.BadRequest) | ||
return@get | ||
} | ||
|
||
val index = meili.client.index(Projects.indexKeyFor(api.parent.guild, project)) | ||
|
||
val result = index.search<IndexDocument>(api.query, null) | ||
.map { DocumentSearchResult(it.references.first(), it.id) } | ||
.take(20) | ||
|
||
call.respond(result) | ||
} | ||
|
||
get<GuildApi.Guild.Document> { api -> | ||
val document = transaction { | ||
DocumentEntity[api.id] | ||
} | ||
|
||
call.respond<DocElement>(document.document) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package dev.triumphteam.docsly.database.entity | ||
|
||
import org.jetbrains.exposed.dao.IntEntity | ||
import org.jetbrains.exposed.dao.IntEntityClass | ||
import org.jetbrains.exposed.dao.id.EntityID | ||
import org.jetbrains.exposed.dao.id.IntIdTable | ||
import org.jetbrains.exposed.sql.Column | ||
|
||
/** | ||
* A database table for storing projects information. | ||
* | ||
* The ProjectsTable class extends the IntIdTable class to inherit the primary key column 'id'. | ||
* It has columns to store the guild id, name, version, and a boolean flag for the latest version. | ||
* The table has a unique constraint on the combination of guild, name, and version columns. | ||
*/ | ||
public object ProjectsTable : IntIdTable("docsly_projects") { | ||
|
||
public val guild: Column<String> = text("guild_id") | ||
public val name: Column<String> = text("name") | ||
public val version: Column<String> = text("version") | ||
public val latest: Column<Boolean> = bool("latest") | ||
|
||
init { | ||
uniqueIndex( | ||
columns = arrayOf(guild, name, version), | ||
customIndexName = "docsly_guild_name_version_uq" | ||
) | ||
} | ||
} | ||
|
||
/** Project entity referencing the table [ProjectsTable]. */ | ||
public class ProjectEntity(entityId: EntityID<Int>) : IntEntity(entityId) { | ||
|
||
public companion object : IntEntityClass<ProjectEntity>(ProjectsTable) | ||
|
||
public var guild: String by ProjectsTable.guild | ||
public var name: String by ProjectsTable.name | ||
public var version: String by ProjectsTable.version | ||
public var latest: Boolean by ProjectsTable.latest | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package dev.triumphteam.docsly.database.exposed | ||
|
||
import org.jetbrains.exposed.dao.Entity | ||
import org.jetbrains.exposed.dao.EntityClass | ||
import org.jetbrains.exposed.dao.id.EntityID | ||
import org.jetbrains.exposed.dao.id.IdTable | ||
|
||
public abstract class StringEntity(id: EntityID<String>) : Entity<String>(id) | ||
|
||
public abstract class StringEntityClass<out E : StringEntity>( | ||
table: IdTable<String>, | ||
entityType: Class<E>? = null, | ||
entityCtor: ((EntityID<String>) -> E)? = null | ||
) : EntityClass<String, E>(table, entityType, entityCtor) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
/** | ||
* MIT License | ||
* | ||
* Copyright (c) 2019-2022 TriumphTeam and Contributors | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in all | ||
* copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
* SOFTWARE. | ||
*/ | ||
package dev.triumphteam.docsly.database.exposed | ||
|
||
import com.impossibl.postgres.jdbc.PGArray | ||
import org.jetbrains.exposed.sql.Column | ||
import org.jetbrains.exposed.sql.ColumnType | ||
import org.jetbrains.exposed.sql.Table | ||
import org.jetbrains.exposed.sql.statements.api.PreparedStatementApi | ||
import org.jetbrains.exposed.sql.statements.jdbc.JdbcPreparedStatementImpl | ||
import org.jetbrains.exposed.sql.vendors.currentDialect | ||
|
||
public fun Table.stringSet(name: String): Column<Set<String>> = | ||
registerColumn(name, StringListColumnType()) | ||
|
||
public class StringListColumnType : ColumnType() { | ||
|
||
override fun sqlType(): String = "${currentDialect.dataTypeProvider.textType()}[]" | ||
|
||
/** When writing the value, it can either be a full on list, or individual values. */ | ||
override fun notNullValueToDB(value: Any): Any = when (value) { | ||
is Collection<*> -> value | ||
is String -> setOf(value) | ||
else -> error("$value of ${value::class.qualifiedName} is not valid for string set") | ||
} | ||
|
||
/** When getting the value it can be more than just [PGArray]. */ | ||
override fun valueFromDB(value: Any): Any = when (value) { | ||
is PGArray -> (value.array as Array<*>).toSet() | ||
is Set<*> -> value | ||
is Array<*> -> value.toSet() | ||
is Collection<*> -> value.toSet() | ||
else -> error("Got unexpected array value of type: ${value::class.qualifiedName} ($value)") | ||
} | ||
|
||
override fun setParameter(stmt: PreparedStatementApi, index: Int, value: Any?) { | ||
if (value == null) { | ||
stmt.setNull(index, this) | ||
} else { | ||
val preparedStatement = stmt as? JdbcPreparedStatementImpl ?: error("Currently only JDBC is supported") | ||
val array = preparedStatement.statement.connection.createArrayOf( | ||
currentDialect.dataTypeProvider.integerType(), | ||
(value as Collection<*>).toTypedArray() | ||
) | ||
stmt[index] = array | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.