Commit 3a242137 authored by Patrick Schlindwein's avatar Patrick Schlindwein
Browse files

#64 Implemented OpenAPI

parent 98cfe1f8
[submodule "src/ktor-server/openapigen"]
path = src/ktor-server/openapigen
url = https://github.com/papsign/Ktor-OpenAPI-Generator.git
......@@ -14,8 +14,11 @@ repositories {
maven {
url "https://kotlin.bintray.com/kotlinx"
}
maven { url 'https://jitpack.io' }
}
apply plugin: 'kotlin'
dependencies {
implementation "io.ktor:ktor-gson:$ktor_version"
implementation "io.ktor:ktor-server-core:$ktor_version"
......@@ -39,6 +42,8 @@ dependencies {
// for logging
implementation "ch.qos.logback:logback-classic:$logback_version"
implementation 'com.github.papsign:Ktor-OpenAPI-Generator:-SNAPSHOT'
// for docx
// https://mvnrepository.com/artifact/org.apache.poi/poi
implementation group: 'org.apache.poi', name: 'poi', version: '5.0.0'
......
Subproject commit 546734c4467036958637a32f4f84c4c4ef044789
rootProject.name = 'ktor-server'
include 'openapigen'
package de.h_da.fbi.smebt.intentfinder.server
import de.h_da.fbi.smebt.intentfinder.server.nlp.PythonBridge
import com.google.gson.GsonBuilder
import com.papsign.ktor.openapigen.OpenAPIGen
import com.papsign.ktor.openapigen.openAPIGen
import com.papsign.ktor.openapigen.route.apiRouting
import com.papsign.ktor.openapigen.route.tag
import com.papsign.ktor.openapigen.schema.namer.DefaultSchemaNamer
import com.papsign.ktor.openapigen.schema.namer.SchemaNamer
import de.h_da.fbi.smebt.intentfinder.server.routing.docx.docxRoutes
import de.h_da.fbi.smebt.intentfinder.server.routing.summarization.summarizationRoutes
import de.h_da.fbi.smebt.intentfinder.server.routing.tags.Tags
import de.h_da.fbi.smebt.intentfinder.server.routing.upload.uploadRoutes
import de.h_da.fbi.smebt.intentfinder.server.upload.registerApiRoutes
import io.ktor.application.*
import io.ktor.features.*
import io.ktor.http.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.serialization.*
import kotlinx.serialization.json.Json
import java.lang.RuntimeException
import de.h_da.fbi.smebt.intentfinder.server.upload.registerApiRoutes
import kotlin.reflect.KType
fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)
fun Application.module() {
install(OpenAPIGen) {
info {
version = "0.0.1"
title = "IntentFinder API"
description = "The IntentFinder API to extract question-answer pairs from URLs or several file types."
contact {
name = "Hochschule Darmstadt PSE SS21"
url = "https://code.fbi.h-da.de/pse-trapp-public/intentfinder"
}
}
server("http://localhost:8080/") {
description = "Local server"
}
replaceModule(DefaultSchemaNamer, object : SchemaNamer {
val regex = Regex("[A-Za-z0-9_.]+")
override fun get(type: KType): String {
return type.toString().replace(regex) { it.value.split(".").last() }.replace(Regex(">|<|, "), "_")
}
})
}
install(ContentNegotiation) {
json()
}
......@@ -30,44 +62,29 @@ fun Application.module() {
}
routing {
post("/summarize") {
val req = call.receive<SummaryRequest>()
val response = PythonBridge().getSummary(req.text, req.maxLength)
call.respond(response)
get("/") {
call.respondRedirect("/swagger-ui/index.html?url=/openapi.json", true)
}
post("/{name}") {
}
// get faq with Json Configuration
get("/faqResource") {
// Rückgabe Json Object (vgl. #38)
}
// Definition eines Endpunkts zur Definition einer Docx-Datei mit JSON-Konfiguration
post("{chatbotId}/docxResource/{filename}") {
get("/openapi.json") {
call.respond(GsonBuilder().create().toJson(application.openAPIGen.api.serialize()))
}
}
// get docx file with Json Configuration
get("/docxResource") {
//Rückgabe Json Object (vgl. #37)
apiRouting {
tag(Tags.SUMMARIZE) {
summarizationRoutes()
}
// Definition endpoint zum Auslesen aller hochgeladener docx mit status
get("/files") {
tag(Tags.INTENT_FINDER) {
uploadRoutes()
docxRoutes()
}
// Routen ohne Funktionalität
routing {
tag(Tags.DEPRECATED) {
registerApiRoutes()
}
}
// registerUploadRoutes()
registerApiRoutes()
}
class InternalServerErrorException : RuntimeException()
......
package de.h_da.fbi.smebt.intentfinder.server.nlp.client
import com.google.gson.JsonObject
import de.h_da.fbi.smebt.intentfinder.server.SummaryRequest
import de.h_da.fbi.smebt.intentfinder.server.nlp.dto.SummaryRequest
import de.h_da.fbi.smebt.intentfinder.server.nlp.dto.Strategies
import de.h_da.fbi.smebt.intentfinder.server.nlp.dto.Summary
import okhttp3.OkHttpClient
......
package de.h_da.fbi.smebt.intentfinder.server.nlp.client
import com.google.gson.JsonObject
import de.h_da.fbi.smebt.intentfinder.server.SummaryRequest
import de.h_da.fbi.smebt.intentfinder.server.nlp.dto.SummaryRequest
import de.h_da.fbi.smebt.intentfinder.server.nlp.dto.Strategies
import de.h_da.fbi.smebt.intentfinder.server.nlp.dto.Summary
import retrofit2.Call
......
package de.h_da.fbi.smebt.intentfinder.server.nlp.dto
import com.papsign.ktor.openapigen.annotations.Response
import kotlinx.serialization.Serializable
@Serializable
@Response("Summary including used strategy and quality.", statusCode = 200)
data class SummaryBody(
val strategy: String,
val quality: Double,
val summary: String,
)
) {
companion object {
val EXAMPLE = SummaryBody("UsedStrategy", 0.9, "My short and good strategy")
}
}
@Serializable
data class Summary(
......
package de.h_da.fbi.smebt.intentfinder.server;
package de.h_da.fbi.smebt.intentfinder.server.nlp.dto;
import kotlinx.serialization.Serializable
......
package de.h_da.fbi.smebt.intentfinder.server.routing.docx
import com.papsign.ktor.openapigen.route.info
import com.papsign.ktor.openapigen.route.path.normal.NormalOpenAPIRoute
import com.papsign.ktor.openapigen.route.path.normal.get
import com.papsign.ktor.openapigen.route.path.normal.post
import com.papsign.ktor.openapigen.route.response.respond
import com.papsign.ktor.openapigen.route.route
import de.h_da.fbi.smebt.intentfinder.server.routing.docx.dto.DocxJsonConfigurationRequest
import kotlinx.serialization.json.*
fun NormalOpenAPIRoute.docxRoutes() {
route("faqResource") {
get<Any, Any>(
info(
summary = "Get FAQ with json configuration",
description = "Not yet implemented, dummy endpoint."
)
) {
// TODO implement
respond(buildJsonObject {
put("key", "some value")
})
}
}
route("{name}") {
post<String, Any, Any> (
info(
summary = "Define docx file",
description = "Define docx file with JSON configuration."
),
exampleRequest = DocxJsonConfigurationRequest.EXAMPLE
) { name, _ ->
// TODO implement
respond(name)
}
}
route("docxResource") {
get<Any, Any>(
info(
summary = "Define docx file",
description = "Define docx file with JSON configuration."
),
) {
// TODO implement
respond(buildJsonObject {
put("key", "some value")
})
}
}
route("files") {
get<Any, List<Any>>(
info(
summary = "Read all uploaded docx file",
description = "Returns all uploaded docx files with status."
),
) {
// TODO implement
respond(listOf("This could be a list of files."))
}
}
}
\ No newline at end of file
package de.h_da.fbi.smebt.intentfinder.server.routing.docx.dto
import com.papsign.ktor.openapigen.annotations.Request
import com.papsign.ktor.openapigen.annotations.parameters.PathParam
import kotlinx.serialization.Required
import kotlinx.serialization.Serializable
@Request("DocxJsonConfiguration request params")
@Serializable
data class DocxJsonConfigurationRequest(
@Required
@PathParam("Chatbot Id")
val chatbotId: String,
@Required
@PathParam("Filename")
val filename: String
) {
companion object {
val EXAMPLE = DocxJsonConfigurationRequest("CB_123", "MyFilename.docx")
}
}
\ No newline at end of file
package de.h_da.fbi.smebt.intentfinder.server.routing.summarization
import com.papsign.ktor.openapigen.route.info
import com.papsign.ktor.openapigen.route.path.normal.NormalOpenAPIRoute
import com.papsign.ktor.openapigen.route.path.normal.post
import com.papsign.ktor.openapigen.route.response.respond
import com.papsign.ktor.openapigen.route.route
import de.h_da.fbi.smebt.intentfinder.server.nlp.PythonBridge
import de.h_da.fbi.smebt.intentfinder.server.nlp.dto.SummaryBody
import de.h_da.fbi.smebt.intentfinder.server.routing.summarization.dto.SummaryPostRequestBody
fun NormalOpenAPIRoute.summarizationRoutes() {
route("summarize") {
post<Any, List<SummaryBody>, SummaryPostRequestBody>(
info(
summary = "Summarize a given text",
description = "Summarizes a given text in consideration of maximum amount of sentences."
),
exampleRequest = SummaryPostRequestBody.EXAMPLE,
exampleResponse = listOf(SummaryBody.EXAMPLE)
) { _, body ->
val response = PythonBridge().getSummary(body.text, body.maxLength)
respond(response)
}
}
}
\ No newline at end of file
package de.h_da.fbi.smebt.intentfinder.server.routing.summarization.dto
import com.papsign.ktor.openapigen.annotations.Request
import kotlinx.serialization.Required
import kotlinx.serialization.Serializable
@Request("Summarize request body.")
@Serializable
data class SummaryPostRequestBody(
@Required
val text: String,
@Required
val maxLength: Int
) {
companion object {
val EXAMPLE = SummaryPostRequestBody("This is my text I want summarized. As maxLength I will pass the maximum amount of sentences the summarization should contain.", 3)
}
}
\ No newline at end of file
package de.h_da.fbi.smebt.intentfinder.server.routing.tags
import com.papsign.ktor.openapigen.APITag
enum class Tags(
override val description: String
) : APITag {
INTENT_FINDER("IntentFinder"),
SUMMARIZE("Summarize"),
DEPRECATED("Deprecated")
}
\ No newline at end of file
package de.h_da.fbi.smebt.intentfinder.server.routing.upload
import com.papsign.ktor.openapigen.route.info
import com.papsign.ktor.openapigen.route.path.normal.NormalOpenAPIRoute
import com.papsign.ktor.openapigen.route.path.normal.post
import com.papsign.ktor.openapigen.route.path.normal.put
import com.papsign.ktor.openapigen.route.route
import de.h_da.fbi.smebt.intentfinder.server.routing.upload.dto.*
fun NormalOpenAPIRoute.uploadRoutes() {
// TODO as soon as those methods are ported remove the `new/` prefix in the routing
route("new/") {
route("upload") {
post<Any, Any, UploadFileRequestBody>(
info(
summary = "Upload file",
description = "Uploads a file"
)
) { _, body ->
// TODO What to actually do here? Code does nothing
// TODO Use file stream instead of multipart stuff
}
}
route("{chatbotId}/file") {
post<UploadDocxRequestParams, FileStatusResponse, UploadDocxRequestBody>(
info(
summary = "Upload a .docx file",
description = "Upload a .docx file and save it."
),
exampleResponse = FileStatusResponse.EXAMPLE
) { params, body ->
// TODO port to proper file handling
}
}
route("{chatbotId}/file/{id}/{filename}") {
put<ModifyDocxRequestParams, FileStatusResponse, UploadDocxRequestBody>(
info(
summary = "Modify a .docx file",
description = "Modify an existing .docx file and save it."
),
exampleResponse = FileStatusResponse.EXAMPLE
) { params, body ->
// TODO port to proper file handling
}
}
route("{chatbotId}/faqResource/{jsonStructure}") {
post<UploadJsonConfigRequestParams, UploadJsonConfigResponse,UploadFileRequestBody>(
info(
summary = "Define a FAQ website",
description = "Define a FAQ website with JSON configuration."
),
exampleResponse = UploadJsonConfigResponse.EXAMPLE
) { params, body ->
// TODO port to proper file handling
}
}
}
}
\ No newline at end of file
package de.h_da.fbi.smebt.intentfinder.server.routing.upload.dto
import com.papsign.ktor.openapigen.annotations.Response
import kotlinx.serialization.Serializable
@Serializable
@Response("File status response.", statusCode = 200)
data class FileStatusResponse(
val path: String,
val status: String
) {
companion object {
val EXAMPLE = FileStatusResponse("uploads/myFile.docx", "created")
}
}
package de.h_da.fbi.smebt.intentfinder.server.routing.upload.dto
import com.papsign.ktor.openapigen.annotations.Request
import com.papsign.ktor.openapigen.annotations.parameters.PathParam
import com.papsign.ktor.openapigen.content.type.multipart.FormDataRequest
import com.papsign.ktor.openapigen.content.type.multipart.NamedFileInputStream
import com.papsign.ktor.openapigen.content.type.multipart.PartEncoding
import kotlinx.serialization.Required
import kotlinx.serialization.Serializable
@FormDataRequest
data class ModifyDocxRequestBody(
@PartEncoding("multipart/form-data")
@Required
val file: NamedFileInputStream
)
@Request("Modify existing .docx request parameters.")
@Serializable
data class ModifyDocxRequestParams(
@Required
@PathParam("Specify Chatbot ID")
val chatbotId: String,
@Required
@PathParam("ID")
val id: String,
@Required
@PathParam("Specify file name")
val filename: String
)
\ No newline at end of file
package de.h_da.fbi.smebt.intentfinder.server.routing.upload.dto
import com.papsign.ktor.openapigen.annotations.Request
import com.papsign.ktor.openapigen.annotations.parameters.PathParam
import com.papsign.ktor.openapigen.content.type.multipart.FormDataRequest
import com.papsign.ktor.openapigen.content.type.multipart.NamedFileInputStream
import com.papsign.ktor.openapigen.content.type.multipart.PartEncoding
import kotlinx.serialization.Required
import kotlinx.serialization.Serializable
@FormDataRequest
data class UploadDocxRequestBody(
@PartEncoding("multipart/form-data")
@Required
val file: NamedFileInputStream
)
@Request("Upload .docx request parameters.")
@Serializable
data class UploadDocxRequestParams(
@Required
@PathParam("Specify Chatbot ID")
val chatbotId: String
)
package de.h_da.fbi.smebt.intentfinder.server.routing.upload.dto
import com.papsign.ktor.openapigen.annotations.Request
import com.papsign.ktor.openapigen.annotations.Response
import com.papsign.ktor.openapigen.annotations.parameters.PathParam
import kotlinx.serialization.Required
import kotlinx.serialization.Serializable
@Request("Upload a JSON config.")
@Serializable
data class UploadJsonConfigRequestParams(
@Required
@PathParam("Title of the JSON config")
val jsonStructure: String
)
@Serializable
@Response("File status response.", statusCode = 200)
data class UploadJsonConfigResponse(
val numberOfPairs: Int
) {
companion object {
val EXAMPLE = UploadJsonConfigResponse(12)
}
}
package de.h_da.fbi.smebt.intentfinder.server.routing.upload.dto
import com.papsign.ktor.openapigen.content.type.multipart.FormDataRequest
import com.papsign.ktor.openapigen.content.type.multipart.NamedFileInputStream
import com.papsign.ktor.openapigen.content.type.multipart.PartEncoding
import kotlinx.serialization.Required
@FormDataRequest
data class UploadFileRequestBody(
@PartEncoding("multipart/form-data")
@Required
val file: NamedFileInputStream
)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment