Skip to content

Commit

Permalink
chore: Re-write project setup handling
Browse files Browse the repository at this point in the history
  • Loading branch information
LichtHund committed Jun 26, 2024
1 parent 284ebbb commit 79ecf61
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 36 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,5 @@ kotlin-js-store/
tailwind/

backend/src/main/resources/static/css/tailwind.css

output/
22 changes: 19 additions & 3 deletions backend/src/main/kotlin/api/ApiRouting.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package dev.triumphteam.backend.api

import dev.triumphteam.backend.DATA_FOLDER
import dev.triumphteam.website.api.Api
import dev.triumphteam.website.project.Repository
import io.ktor.http.HttpStatusCode
import io.ktor.http.content.PartData
import io.ktor.http.content.forEachPart
import io.ktor.http.content.streamProvider
import io.ktor.server.application.call
import io.ktor.server.auth.authenticate
import io.ktor.server.request.receive
import io.ktor.server.request.receiveMultipart
import io.ktor.server.resources.post
import io.ktor.server.response.respond
import io.ktor.server.routing.Routing
Expand All @@ -19,11 +24,22 @@ public fun Routing.apiRoutes() {
authenticate("bearer") {
post<Api.Setup> {
runCatching {
call.receive<Repository>()
call.receiveMultipart()
}.fold(
onSuccess = { (projects) ->
onSuccess = { multipartData ->
// Handle parsing
setupRepository(projects)
multipartData.forEachPart { part ->
when (part) {
is PartData.FileItem -> {
val fileBytes = part.streamProvider().readBytes()
DATA_FOLDER.resolve("downloads/projects.zip").writeBytes(fileBytes)
}

else -> {}
}
part.dispose()
}

call.respond(HttpStatusCode.Accepted)
},
onFailure = {
Expand Down
3 changes: 1 addition & 2 deletions backend/src/main/kotlin/api/ProjectSetup.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@ public fun setupRepository(projects: List<Project>) {

val projectEntity = ProjectEntity.new(project.id) {
this.name = project.name
this.icon = project.icon
this.color = project.color
this.github = project.projectHome
}

val projectIcon = ImageIO.read(URL(project.icon))
val projectIcon = ImageIO.read(URL(""))

project.versions.forEach { version ->

Expand Down
1 change: 0 additions & 1 deletion common/src/main/kotlin/project/SerialRepresentation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ public data class Repository(
public data class Project(
public val id: String,
public val name: String,
public val icon: String,
public val color: String,
public val projectHome: String,
public val versions: List<DocVersion>,
Expand Down
2 changes: 2 additions & 0 deletions docs/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ dependencies {
implementation(libs.bundles.logger)
implementation(libs.bundles.commonmark)
implementation(libs.commons.cli)

implementation("net.lingala.zip4j:zip4j:2.11.5")
}
113 changes: 84 additions & 29 deletions docs/src/main/kotlin/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,20 @@ import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.onUpload
import io.ktor.client.plugins.resources.Resources
import io.ktor.client.plugins.resources.post
import io.ktor.client.request.bearerAuth
import io.ktor.client.request.forms.MultiPartFormDataContent
import io.ktor.client.request.forms.formData
import io.ktor.client.request.setBody
import io.ktor.http.ContentType
import io.ktor.http.Headers
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.http.contentType
import io.ktor.serialization.kotlinx.json.json
import io.ktor.util.InternalAPI
import net.lingala.zip4j.ZipFile
import org.apache.commons.cli.DefaultParser
import org.apache.commons.cli.Option
import org.apache.commons.cli.Options
Expand Down Expand Up @@ -63,6 +69,7 @@ private val mdParser = Parser.builder().extensions(DEFAULT_EXTENSIONS).build()

private val logger: Logger = LoggerFactory.getLogger("docs")

@OptIn(InternalAPI::class)
public suspend fun main(args: Array<String>) {

val options = DefaultParser().parse(
Expand Down Expand Up @@ -90,33 +97,56 @@ public suspend fun main(args: Array<String>) {
HoconSerializer.from<RepoSettings>(requireNotNull(inputFiles.find { it.name == "settings.conf" }))

// Navigate through file structure and parse all projects
val repo = Repository(projects = inputFiles.mapNotNull { projectDir ->
// Ignore non-directory files
if (!projectDir.isDirectory) return@mapNotNull null
val projects = Projects(
projects = inputFiles.mapNotNull { projectDir ->
// Ignore non-directory files
if (!projectDir.isDirectory) return@mapNotNull null

val files = projectDir.listFiles() ?: emptyArray()
val projectConfig = files.findFile("project.conf") {
"Found project folder without a 'project.conf' file, skipping it!"
} ?: return@mapNotNull null

val files = projectDir.listFiles() ?: emptyArray()
val projectConfig = files.find("project.conf") {
"Found project folder without a 'project.conf' file, skipping it!"
} ?: return@mapNotNull null
val parsedProjectConfig = HoconSerializer.from<ProjectConfig>(projectConfig)

ProjectWithIcon(
project = Project(
id = parsedProjectConfig.id,
name = parsedProjectConfig.name,
color = parsedProjectConfig.color,
projectHome = parsedProjectConfig.projectHome,
versions = parseVersions(files.filter(File::isDirectory), inputPath, repoSettings),
),
icon = requireNotNull(files.findFile("icon.png")) {
"Found project folder without a 'project.conf' file, skipping it!"
}
).also {
logger.info("Parsed project '${it.project.id}', with versions: ${it.project.versions.map(DocVersion::reference)}!")
}
},
)

val parsedProjectConfig = HoconSerializer.from<ProjectConfig>(projectConfig)

Project(
id = parsedProjectConfig.id,
name = parsedProjectConfig.name,
icon = parsedProjectConfig.icon,
color = parsedProjectConfig.color,
projectHome = parsedProjectConfig.projectHome,
versions = parseVersions(files.filter(File::isDirectory), inputPath, repoSettings),
).also {
logger.info("Parsed project '$${it.id}', with versions: ${it.versions.map(DocVersion::reference)}!")
val outputDir = File("output").also(File::mkdirs)

val repository = outputDir.resolve("repository.json").also {
it.writeText(JsonSerializer.encode<Repository>(projects.toRepository()))
}

val icons = projects.projects.map {
outputDir.resolve(it.project.id).also { dir ->
dir.mkdirs()
it.icon.copyTo(dir.resolve("icon.png"), overwrite = true)
}
})
}

println(JsonSerializer.encode<Repository>(repo))
return
val zip = ZipFile("projects.zip").also {
icons.forEach { dir ->
it.addFolder(dir)
}
it.addFile(repository)
}

logger.info("Paring complete!")
logger.info("Parsing complete!")
logger.info("Uploading..")

val client = HttpClient(CIO) {
Expand All @@ -128,12 +158,24 @@ public suspend fun main(args: Array<String>) {
defaultRequest {
url(url)
bearerAuth(bearer)
contentType(ContentType.Application.Json)
}
}

val response = client.post(Api.Setup()) {
setBody(repo)
setBody(
MultiPartFormDataContent(
formData {
append("zip", zip.file.readBytes(), Headers.build {
append(HttpHeaders.ContentType, ContentType.Application.Zip)
append(HttpHeaders.ContentDisposition, "filename=\"projects.zip\"")
})
},
boundary = "WebAppBoundary"
)
)
onUpload { bytesSentTotal, contentLength ->
logger.info("Sent $bytesSentTotal bytes from $contentLength")
}
}

if (response.status != HttpStatusCode.Accepted) {
Expand All @@ -148,7 +190,7 @@ private fun parseVersions(versionDirs: List<File>, parentDir: File, repoSettings
return versionDirs.mapNotNull { versionDir ->

val files = versionDir.listFiles() ?: emptyArray()
val versionConfig = files.find("version.conf") {
val versionConfig = files.findFile("version.conf") {
"Found version folder without a 'version.conf' file, skipping it!"
} ?: return@mapNotNull null

Expand All @@ -159,7 +201,7 @@ private fun parseVersions(versionDirs: List<File>, parentDir: File, repoSettings

files.filter(File::isDirectory).sortedBy(File::getName).forEach { groupDir ->
val groupFiles = groupDir.listFiles() ?: emptyArray()
val groupConfig = groupFiles.find("group.conf") {
val groupConfig = groupFiles.findFile("group.conf") {
"Found group folder without a 'group.conf' file, skipping it!"
} ?: return@mapNotNull null

Expand Down Expand Up @@ -212,13 +254,26 @@ private fun parseVersions(versionDirs: List<File>, parentDir: File, repoSettings
}
}

private fun Array<File>.find(fileName: String, log: () -> String): File? {
private fun Array<File>.findFile(fileName: String, log: (() -> String)? = null): File? {
val file = find { it.name == fileName }

if (file == null) {
logger.warn(log())
log?.invoke()?.let { logger.warn(it) }
return null
}

return file
}

private data class ProjectWithIcon(val project: Project, val icon: File)

private data class Projects(val projects: List<ProjectWithIcon>) {

fun toRepository(): Repository {
return Repository(projects.map(ProjectWithIcon::project))
}

fun icons(): List<File> {
return projects.map(ProjectWithIcon::icon)
}
}
1 change: 0 additions & 1 deletion docs/src/main/kotlin/serialization/FileRepresentation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ public data class RepoSettings(public val editPath: String)
public data class ProjectConfig(
public val id: String,
public val name: String,
public val icon: String,
public val color: String,
public val projectHome: String,
)
Expand Down

0 comments on commit 79ecf61

Please sign in to comment.