style: Lint code

This commit is contained in:
oSumAtrIX 2024-02-06 00:21:58 +01:00
parent 6b34aaf5db
commit e798a4c070
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
13 changed files with 156 additions and 106 deletions

3
.editorconfig Normal file
View File

@ -0,0 +1,3 @@
[*.{kt,kts}]
ktlint_code_style = intellij_idea
ktlint_standard_no-wildcard-imports = disabled

View File

@ -11,7 +11,7 @@ import kotlinx.serialization.Serializable
* @param httpClientConfig The configuration of the HTTP client. * @param httpClientConfig The configuration of the HTTP client.
*/ */
abstract class Backend( abstract class Backend(
httpClientConfig: HttpClientConfig<OkHttpConfig>.() -> Unit = {} httpClientConfig: HttpClientConfig<OkHttpConfig>.() -> Unit = {},
) { ) {
protected val client: HttpClient = HttpClient(OkHttp, httpClientConfig) protected val client: HttpClient = HttpClient(OkHttp, httpClientConfig)
@ -34,7 +34,7 @@ abstract class Backend(
* @property members The members of the organization. * @property members The members of the organization.
*/ */
class BackendOrganization( class BackendOrganization(
val members: Set<BackendMember> val members: Set<BackendMember>,
) { ) {
/** /**
* A member of an organization. * A member of an organization.
@ -51,7 +51,7 @@ abstract class Backend(
override val avatarUrl: String, override val avatarUrl: String,
override val url: String, override val url: String,
val bio: String?, val bio: String?,
val gpgKeysUrl: String val gpgKeysUrl: String,
) : BackendUser ) : BackendUser
/** /**
@ -60,7 +60,7 @@ abstract class Backend(
* @property contributors The contributors of the repository. * @property contributors The contributors of the repository.
*/ */
class BackendRepository( class BackendRepository(
val contributors: Set<BackendContributor> val contributors: Set<BackendContributor>,
) { ) {
/** /**
* A contributor of a repository. * A contributor of a repository.
@ -75,7 +75,7 @@ abstract class Backend(
override val name: String, override val name: String,
override val avatarUrl: String, override val avatarUrl: String,
override val url: String, override val url: String,
val contributions: Int val contributions: Int,
) : BackendUser ) : BackendUser
/** /**
@ -91,7 +91,7 @@ abstract class Backend(
val tag: String, val tag: String,
val releaseNote: String, val releaseNote: String,
val createdAt: LocalDateTime, val createdAt: LocalDateTime,
val assets: Set<BackendAsset> val assets: Set<BackendAsset>,
) { ) {
/** /**
* An asset of a release. * An asset of a release.
@ -100,7 +100,7 @@ abstract class Backend(
*/ */
@Serializable @Serializable
class BackendAsset( class BackendAsset(
val downloadUrl: String val downloadUrl: String,
) )
} }
} }

View File

@ -1,7 +1,18 @@
package app.revanced.api.backend.github package app.revanced.api.backend.github
import app.revanced.api.backend.Backend import app.revanced.api.backend.Backend
import app.revanced.api.backend.Backend.BackendOrganization.BackendMember
import app.revanced.api.backend.Backend.BackendOrganization.BackendRepository.BackendContributor
import app.revanced.api.backend.Backend.BackendOrganization.BackendRepository.BackendRelease
import app.revanced.api.backend.Backend.BackendOrganization.BackendRepository.BackendRelease.BackendAsset
import app.revanced.api.backend.github.api.Request import app.revanced.api.backend.github.api.Request
import app.revanced.api.backend.github.api.Request.Organization.Members
import app.revanced.api.backend.github.api.Request.Organization.Repository.Contributors
import app.revanced.api.backend.github.api.Request.Organization.Repository.Releases
import app.revanced.api.backend.github.api.Response
import app.revanced.api.backend.github.api.Response.GitHubOrganization.GitHubMember
import app.revanced.api.backend.github.api.Response.GitHubOrganization.GitHubRepository.GitHubContributor
import app.revanced.api.backend.github.api.Response.GitHubOrganization.GitHubRepository.GitHubRelease
import io.ktor.client.call.* import io.ktor.client.call.*
import io.ktor.client.plugins.* import io.ktor.client.plugins.*
import io.ktor.client.plugins.auth.* import io.ktor.client.plugins.auth.*
@ -9,17 +20,6 @@ import io.ktor.client.plugins.auth.providers.*
import io.ktor.client.plugins.cache.* import io.ktor.client.plugins.cache.*
import io.ktor.client.plugins.contentnegotiation.* import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.resources.* import io.ktor.client.plugins.resources.*
import app.revanced.api.backend.Backend.BackendOrganization.BackendMember
import app.revanced.api.backend.Backend.BackendOrganization.BackendRepository.BackendRelease
import app.revanced.api.backend.Backend.BackendOrganization.BackendRepository.BackendContributor
import app.revanced.api.backend.Backend.BackendOrganization.BackendRepository.BackendRelease.BackendAsset
import app.revanced.api.backend.github.api.Request.Organization.Repository.Releases
import app.revanced.api.backend.github.api.Request.Organization.Repository.Contributors
import app.revanced.api.backend.github.api.Request.Organization.Members
import app.revanced.api.backend.github.api.Response
import app.revanced.api.backend.github.api.Response.GitHubOrganization.GitHubRepository.GitHubRelease
import app.revanced.api.backend.github.api.Response.GitHubOrganization.GitHubRepository.GitHubContributor
import app.revanced.api.backend.github.api.Response.GitHubOrganization.GitHubMember
import io.ktor.client.plugins.resources.Resources import io.ktor.client.plugins.resources.Resources
import io.ktor.serialization.kotlinx.json.* import io.ktor.serialization.kotlinx.json.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
@ -28,18 +28,18 @@ import kotlinx.datetime.toLocalDateTime
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonNamingStrategy import kotlinx.serialization.json.JsonNamingStrategy
import org.koin.dsl.bind
import org.koin.dsl.module
@OptIn(ExperimentalSerializationApi::class) @OptIn(ExperimentalSerializationApi::class)
class GitHubBackend(token: String? = null) : Backend({ class GitHubBackend(token: String? = null) : Backend({
install(HttpCache) install(HttpCache)
install(Resources) install(Resources)
install(ContentNegotiation) { install(ContentNegotiation) {
json(Json { json(
Json {
ignoreUnknownKeys = true ignoreUnknownKeys = true
namingStrategy = JsonNamingStrategy.SnakeCase namingStrategy = JsonNamingStrategy.SnakeCase
}) },
)
} }
defaultRequest { url("https://api.github.com") } defaultRequest { url("https://api.github.com") }
@ -50,7 +50,7 @@ class GitHubBackend(token: String? = null) : Backend({
loadTokens { loadTokens {
BearerTokens( BearerTokens(
accessToken = it, accessToken = it,
refreshToken = "" // Required dummy value refreshToken = "", // Required dummy value
) )
} }
@ -64,11 +64,11 @@ class GitHubBackend(token: String? = null) : Backend({
repository: String, repository: String,
tag: String?, tag: String?,
): BackendRelease { ): BackendRelease {
val release: GitHubRelease = if (tag != null) val release: GitHubRelease = if (tag != null) {
client.get(Releases.Tag(owner, repository, tag)).body() client.get(Releases.Tag(owner, repository, tag)).body()
else } else {
client.get(Releases.Latest(owner, repository)).body() client.get(Releases.Latest(owner, repository)).body()
}
return BackendRelease( return BackendRelease(
tag = release.tagName, tag = release.tagName,
@ -76,13 +76,13 @@ class GitHubBackend(token: String? = null) : Backend({
createdAt = release.createdAt.toLocalDateTime(TimeZone.UTC), createdAt = release.createdAt.toLocalDateTime(TimeZone.UTC),
assets = release.assets.map { assets = release.assets.map {
BackendAsset(downloadUrl = it.browserDownloadUrl) BackendAsset(downloadUrl = it.browserDownloadUrl)
}.toSet() }.toSet(),
) )
} }
override suspend fun getContributors( override suspend fun getContributors(
owner: String, owner: String,
repository: String repository: String,
): Set<BackendContributor> { ): Set<BackendContributor> {
val contributors: Set<GitHubContributor> = client.get(Contributors(owner, repository)).body() val contributors: Set<GitHubContributor> = client.get(Contributors(owner, repository)).body()
@ -91,7 +91,7 @@ class GitHubBackend(token: String? = null) : Backend({
name = it.login, name = it.login,
avatarUrl = it.avatarUrl, avatarUrl = it.avatarUrl,
url = it.url, url = it.url,
contributions = it.contributions contributions = it.contributions,
) )
}.toSet() }.toSet()
} }

View File

@ -5,6 +5,7 @@ import io.ktor.resources.*
class Request { class Request {
@Resource("/users/{username}") @Resource("/users/{username}")
class User(val username: String) class User(val username: String)
class Organization { class Organization {
@Resource("/orgs/{org}/members") @Resource("/orgs/{org}/members")
class Members(val org: String) class Members(val org: String)

View File

@ -3,7 +3,6 @@ package app.revanced.api.backend.github.api
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
class Response { class Response {
interface IGitHubUser { interface IGitHubUser {
val login: String val login: String
@ -41,11 +40,11 @@ class Response {
val tagName: String, val tagName: String,
val assets: Set<GitHubAsset>, val assets: Set<GitHubAsset>,
val createdAt: Instant, val createdAt: Instant,
val body: String val body: String,
) { ) {
@Serializable @Serializable
class GitHubAsset( class GitHubAsset(
val browserDownloadUrl: String val browserDownloadUrl: String,
) )
} }
} }

View File

@ -1,11 +1,10 @@
package app.revanced.api.modules package app.revanced.api.modules
import app.revanced.api.modules.AnnouncementService.Attachments.announcement import app.revanced.api.modules.AnnouncementService.Attachments.announcement
import app.revanced.api.schema.APIResponseAnnouncement
import app.revanced.api.schema.APIAnnouncement import app.revanced.api.schema.APIAnnouncement
import app.revanced.api.schema.APILatestAnnouncement import app.revanced.api.schema.APILatestAnnouncement
import app.revanced.api.schema.APIResponseAnnouncement
import kotlinx.datetime.* import kotlinx.datetime.*
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.dao.IntEntity import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID import org.jetbrains.exposed.dao.id.EntityID
@ -13,7 +12,7 @@ import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.kotlin.datetime.datetime import org.jetbrains.exposed.sql.kotlin.datetime.datetime
import org.jetbrains.exposed.sql.transactions.transaction
class AnnouncementService(private val database: Database) { class AnnouncementService(private val database: Database) {
private object Announcements : IntIdTable() { private object Announcements : IntIdTable() {
@ -52,7 +51,7 @@ class AnnouncementService(private val database: Database) {
channel, channel,
createdAt, createdAt,
archivedAt, archivedAt,
level level,
) )
} }
@ -107,7 +106,7 @@ class AnnouncementService(private val database: Database) {
fun archive( fun archive(
id: Int, id: Int,
archivedAt: LocalDateTime? archivedAt: LocalDateTime?,
) = transaction { ) = transaction {
Announcement.findById(id)?.apply { Announcement.findById(id)?.apply {
this.archivedAt = archivedAt ?: java.time.LocalDateTime.now().toKotlinLocalDateTime() this.archivedAt = archivedAt ?: java.time.LocalDateTime.now().toKotlinLocalDateTime()

View File

@ -19,7 +19,7 @@ fun Application.configureDependencies() {
globalModule, globalModule,
gitHubBackendModule, gitHubBackendModule,
databaseModule, databaseModule,
authModule authModule,
) )
} }
} }
@ -51,7 +51,7 @@ val databaseModule = module {
url = dotenv["DB_URL"], url = dotenv["DB_URL"],
user = dotenv["DB_USER"], user = dotenv["DB_USER"],
password = dotenv["DB_PASSWORD"], password = dotenv["DB_PASSWORD"],
driver = "org.h2.Driver" driver = "org.h2.Driver",
) )
} }
factory<AnnouncementService> { factory<AnnouncementService> {

View File

@ -23,41 +23,68 @@ fun Application.configureRouting() {
routing { routing {
route("/v${configuration.apiVersion}") { route("/v${configuration.apiVersion}") {
route("/announcements") { route("/announcements") {
suspend fun PipelineContext<*, ApplicationCall>.announcement( suspend fun PipelineContext<*, ApplicationCall>.announcement(block: AnnouncementService.() -> APIResponseAnnouncement?) =
block: AnnouncementService.() -> APIResponseAnnouncement? announcementService.block()?.let { call.respond(it) }
) = announcementService.block()?.let { call.respond(it) }
?: call.respond(HttpStatusCode.NotFound) ?: call.respond(HttpStatusCode.NotFound)
suspend fun PipelineContext<*, ApplicationCall>.announcementId( suspend fun PipelineContext<*, ApplicationCall>.announcementId(block: AnnouncementService.() -> APILatestAnnouncement?) =
block: AnnouncementService.() -> APILatestAnnouncement? announcementService.block()?.let { call.respond(it) }
) = announcementService.block()?.let { call.respond(it) }
?: call.respond(HttpStatusCode.NotFound) ?: call.respond(HttpStatusCode.NotFound)
suspend fun PipelineContext<*, ApplicationCall>.channel(block: suspend (String) -> Unit) = suspend fun PipelineContext<*, ApplicationCall>.channel(block: suspend (String) -> Unit) =
block(call.parameters["channel"]!!) block(call.parameters["channel"]!!)
route("/{channel}/latest") { route("/{channel}/latest") {
get("/id") { channel { announcementId { latestId(it) } } } get("/id") {
channel {
get { channel { announcement { latest(it) } } } announcementId {
latestId(it)
}
}
} }
get("/{channel}") { channel { call.respond(announcementService.read(it)) } } get {
channel {
announcement {
latest(it)
}
}
}
}
get("/{channel}") {
channel {
call.respond(announcementService.read(it))
}
}
route("/latest") { route("/latest") {
get("/id") { announcementId { latestId() } } get("/id") {
announcementId {
get { announcement { latest() } } latestId()
}
} }
get { call.respond(announcementService.read()) } get {
announcement {
latest()
}
}
}
get {
call.respond(announcementService.read())
}
authenticate("jwt") { authenticate("jwt") {
suspend fun PipelineContext<*, ApplicationCall>.id(block: suspend (Int) -> Unit) = suspend fun PipelineContext<*, ApplicationCall>.id(block: suspend (Int) -> Unit) =
call.parameters["id"]!!.toIntOrNull()?.let { block(it) } call.parameters["id"]!!.toIntOrNull()?.let {
?: call.respond(HttpStatusCode.BadRequest) block(it)
} ?: call.respond(HttpStatusCode.BadRequest)
post { announcementService.new(call.receive<APIAnnouncement>()) } post {
announcementService.new(call.receive<APIAnnouncement>())
}
post("/{id}/archive") { post("/{id}/archive") {
id { id {
@ -66,11 +93,23 @@ fun Application.configureRouting() {
} }
} }
post("/{id}/unarchive") { id { announcementService.unarchive(it) } } post("/{id}/unarchive") {
id {
announcementService.unarchive(it)
}
}
patch("/{id}") { id { announcementService.update(it, call.receive<APIAnnouncement>()) } } patch("/{id}") {
id {
announcementService.update(it, call.receive<APIAnnouncement>())
}
}
delete("/{id}") { id { announcementService.delete(it) } } delete("/{id}") {
id {
announcementService.delete(it)
}
}
} }
} }
@ -78,19 +117,22 @@ fun Application.configureRouting() {
route("latest") { route("latest") {
get { get {
val patches = backend.getRelease(configuration.organization, configuration.patchesRepository) val patches = backend.getRelease(configuration.organization, configuration.patchesRepository)
val integrations = configuration.integrationsRepositoryNames.map { val integrations =
configuration.integrationsRepositoryNames.map {
async { backend.getRelease(configuration.organization, it) } async { backend.getRelease(configuration.organization, it) }
}.awaitAll() }.awaitAll()
val assets = (patches.assets + integrations.flatMap { it.assets }).filter { val assets =
(patches.assets + integrations.flatMap { it.assets }).filter {
it.downloadUrl.endsWith(".apk") || it.downloadUrl.endsWith(".jar") it.downloadUrl.endsWith(".apk") || it.downloadUrl.endsWith(".jar")
}.map { APIAsset(it.downloadUrl) }.toSet() }.map { APIAsset(it.downloadUrl) }.toSet()
val release = APIRelease( val release =
APIRelease(
patches.tag, patches.tag,
patches.createdAt, patches.createdAt,
patches.releaseNote, patches.releaseNote,
assets assets,
) )
call.respond(release) call.respond(release)
@ -112,13 +154,14 @@ fun Application.configureRouting() {
} }
get("/contributors") { get("/contributors") {
val contributors = configuration.contributorsRepositoryNames.map { val contributors =
configuration.contributorsRepositoryNames.map {
async { async {
APIContributable( APIContributable(
it, it,
backend.getContributors(configuration.organization, it).map { backend.getContributors(configuration.organization, it).map {
APIContributor(it.name, it.avatarUrl, it.url, it.contributions) APIContributor(it.name, it.avatarUrl, it.url, it.contributions)
}.toSet() }.toSet(),
) )
} }
}.awaitAll() }.awaitAll()
@ -127,7 +170,8 @@ fun Application.configureRouting() {
} }
get("/team") { get("/team") {
val team = backend.getMembers(configuration.organization).map { val team =
backend.getMembers(configuration.organization).map {
APIMember(it.name, it.avatarUrl, it.url, it.gpgKeysUrl) APIMember(it.name, it.avatarUrl, it.url, it.gpgKeysUrl)
} }
@ -140,7 +184,11 @@ fun Application.configureRouting() {
} }
} }
authenticate("basic") { get("/token") { call.respond(authService.newToken()) } } authenticate("basic") {
get("/token") {
call.respond(authService.newToken())
}
}
} }
} }
} }

View File

@ -22,7 +22,7 @@ class AuthService(
verifier( verifier(
JWT.require(Algorithm.HMAC256(jwtSecret)) JWT.require(Algorithm.HMAC256(jwtSecret))
.withIssuer(issuer) .withIssuer(issuer)
.build() .build(),
) )
validate { credential -> JWTPrincipal(credential.payload) } validate { credential -> JWTPrincipal(credential.payload) }
} }

View File

@ -8,7 +8,7 @@ class APIRelease(
val version: String, val version: String,
val createdAt: LocalDateTime, val createdAt: LocalDateTime,
val changelog: String, val changelog: String,
val assets: Set<APIAsset> val assets: Set<APIAsset>,
) )
interface APIUser { interface APIUser {
@ -22,7 +22,7 @@ class APIMember(
override val name: String, override val name: String,
override val avatarUrl: String, override val avatarUrl: String,
override val url: String, override val url: String,
val gpgKeysUrl: String val gpgKeysUrl: String,
) : APIUser ) : APIUser
@Serializable @Serializable
@ -36,7 +36,7 @@ class APIContributor(
@Serializable @Serializable
class APIContributable( class APIContributable(
val name: String, val name: String,
val contributors: Set<APIContributor> val contributors: Set<APIContributor>,
) )
@Serializable @Serializable
@ -52,7 +52,7 @@ class APIAsset(
@Serializable @Serializable
class APIReleaseVersion( class APIReleaseVersion(
val version: String val version: String,
) )
@Serializable @Serializable
@ -63,7 +63,7 @@ class APIAnnouncement(
val attachmentUrls: Set<String> = emptySet(), val attachmentUrls: Set<String> = emptySet(),
val channel: String? = null, val channel: String? = null,
val archivedAt: LocalDateTime? = null, val archivedAt: LocalDateTime? = null,
val level: Int = 0 val level: Int = 0,
) )
@Serializable @Serializable
@ -76,15 +76,15 @@ class APIResponseAnnouncement(
val channel: String? = null, val channel: String? = null,
val createdAt: LocalDateTime, val createdAt: LocalDateTime,
val archivedAt: LocalDateTime? = null, val archivedAt: LocalDateTime? = null,
val level: Int = 0 val level: Int = 0,
) )
@Serializable @Serializable
class APILatestAnnouncement( class APILatestAnnouncement(
val id: Int val id: Int,
) )
@Serializable @Serializable
class APIAnnouncementArchivedAt( class APIAnnouncementArchivedAt(
val archivedAt: LocalDateTime val archivedAt: LocalDateTime,
) )

View File

@ -13,5 +13,5 @@ class APIConfiguration(
@SerialName("contributors-repositories") @SerialName("contributors-repositories")
val contributorsRepositoryNames: Set<String>, val contributorsRepositoryNames: Set<String>,
@SerialName("api-version") @SerialName("api-version")
val apiVersion: Int = 1 val apiVersion: Int = 1,
) )

View File

@ -23,8 +23,8 @@ class ApplicationTest {
organization = "ReVanced", organization = "ReVanced",
patchesRepository = "", patchesRepository = "",
integrationsRepositoryNames = setOf(), integrationsRepositoryNames = setOf(),
contributorsRepositoryNames = setOf() contributorsRepositoryNames = setOf(),
) ),
).let(::writeText) ).let(::writeText)
deleteOnExit() deleteOnExit()
@ -47,7 +47,7 @@ class ApplicationTest {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
"Basic ${"${dotenv["BASIC_USERNAME"]}:${dotenv["BASIC_PASSWORD"]}".encodeBase64()}" "Basic ${"${dotenv["BASIC_USERNAME"]}:${dotenv["BASIC_PASSWORD"]}".encodeBase64()}",
) )
} }
}.bodyAsText() }.bodyAsText()