From d3bcff470c33e2b32747d53b2dac24966531128c Mon Sep 17 00:00:00 2001 From: Hubert Van De Walle Date: Sun, 27 Sep 2020 17:49:56 +0200 Subject: [PATCH] Refactor + // img download --- pom.xml | 16 +-- src/main/kotlin/C2c.kt | 102 ------------------ src/main/kotlin/Camp2Camp.kt | 53 +++++++++ src/main/kotlin/Modules.kt | 53 +++++++++ src/main/kotlin/api/ImageDownloader.kt | 24 +++++ src/main/kotlin/filters/Filters.kt | 26 +++++ src/main/kotlin/pdf/PdfCreator.kt | 48 +-------- src/main/kotlin/pdf/PreloadedStreamFactory.kt | 12 +++ .../{IndexRoute.kt => IndexRouteProvider.kt} | 12 +-- src/main/kotlin/routes/PdfRoute.kt | 38 ------- src/main/kotlin/routes/PdfRouteProvider.kt | 39 +++++++ src/main/kotlin/routes/RouteProvider.kt | 7 ++ .../{RouteRoute.kt => RouteRouteProvider.kt} | 5 +- ...{SearchRoute.kt => SearchRouteProvider.kt} | 7 +- .../kotlin/{ => server}/CustomApacheServer.kt | 11 +- src/main/kotlin/templates/Templates.kt | 16 ++- src/main/resources/templates/index.twig | 29 +++-- src/main/resources/templates/pdf.twig | 10 ++ src/main/resources/views/index.twig | 19 ---- 19 files changed, 280 insertions(+), 247 deletions(-) delete mode 100644 src/main/kotlin/C2c.kt create mode 100644 src/main/kotlin/Camp2Camp.kt create mode 100644 src/main/kotlin/Modules.kt create mode 100644 src/main/kotlin/api/ImageDownloader.kt create mode 100644 src/main/kotlin/filters/Filters.kt create mode 100644 src/main/kotlin/pdf/PreloadedStreamFactory.kt rename src/main/kotlin/routes/{IndexRoute.kt => IndexRouteProvider.kt} (55%) delete mode 100644 src/main/kotlin/routes/PdfRoute.kt create mode 100644 src/main/kotlin/routes/PdfRouteProvider.kt create mode 100644 src/main/kotlin/routes/RouteProvider.kt rename src/main/kotlin/routes/{RouteRoute.kt => RouteRouteProvider.kt} (82%) rename src/main/kotlin/routes/{SearchRoute.kt => SearchRouteProvider.kt} (83%) rename src/main/kotlin/{ => server}/CustomApacheServer.kt (76%) create mode 100644 src/main/resources/templates/pdf.twig delete mode 100644 src/main/resources/views/index.twig diff --git a/pom.xml b/pom.xml index 5db1ea3..bd68090 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ ${java.version} ${java.version} UTF-8 - be.simplenotes.c2c/C2cKt + be.simplenotes.c2c/Camp2CampKt 1.0.4 @@ -204,20 +204,6 @@ ${main.class} - - - com.github.ben-manes.caffeine:caffeine - - ** - - - - org.jetbrains.kotlin:kotlin-reflect - - ** - - - diff --git a/src/main/kotlin/C2c.kt b/src/main/kotlin/C2c.kt deleted file mode 100644 index ede849f..0000000 --- a/src/main/kotlin/C2c.kt +++ /dev/null @@ -1,102 +0,0 @@ -package be.simplenotes.c2c - -import be.simplenotes.c2c.api.Api -import be.simplenotes.c2c.api.ApiUrls -import be.simplenotes.c2c.pdf.PdfCreator -import be.simplenotes.c2c.pdf.StreamFactory -import be.simplenotes.c2c.routes.IndexRoute -import be.simplenotes.c2c.routes.PdfRoute -import be.simplenotes.c2c.routes.RouteRoute -import be.simplenotes.c2c.routes.SearchRoute -import com.mitchellbosecke.pebble.PebbleEngine -import com.mitchellbosecke.pebble.loader.ClasspathLoader -import org.apache.hc.client5.http.impl.DefaultRedirectStrategy -import org.apache.hc.client5.http.impl.classic.HttpClientBuilder -import org.http4k.client.ApacheClient -import org.http4k.contract.ContractRoute -import org.http4k.contract.contract -import org.http4k.contract.openapi.ApiInfo -import org.http4k.contract.openapi.v3.OpenApi3 -import org.http4k.core.Filter -import org.http4k.core.Response -import org.http4k.core.Status -import org.http4k.core.then -import org.http4k.filter.RequestFilters -import org.http4k.filter.ServerFilters -import org.http4k.format.Jackson -import org.http4k.server.asServer -import org.koin.core.context.startKoin -import org.koin.core.qualifier.named -import org.koin.dsl.bind -import org.koin.dsl.module -import org.slf4j.LoggerFactory -import java.io.PrintWriter -import java.io.StringWriter - -val module = module { - single { - ApacheClient( - HttpClientBuilder - .create() - .setRedirectStrategy(DefaultRedirectStrategy()) - .build() - ) - } - single { ApiUrls(get()) } - single { Jackson.mapper } - single { Api(get(), get()) } - single { PdfCreator(get()) } - single { StreamFactory(get()) } - single { - PebbleEngine - .Builder() - .loader(ClasspathLoader().apply { - prefix = "views/" - suffix = ".twig" - }) - .cacheActive(false) - .build() - } -} - -val routes = module { - single(named()) { SearchRoute(get()).searchRoute() } bind ContractRoute::class - single(named()) { RouteRoute(get()).routeRoute() } bind ContractRoute::class - single(named()) { PdfRoute(get(), get()).route() } bind ContractRoute::class - single(named()) { IndexRoute(get()).route() } bind ContractRoute::class -} - -fun main() { - val koin = startKoin { - modules(module, routes) - }.koin - - val appRoutes = koin.getAll() - val app = contract { - renderer = OpenApi3(ApiInfo("Camp2Camp", "1.0-SNAPSHOT"), Jackson) - descriptionPath = "/api/swagger.json" - routes.all.addAll(appRoutes) - } - - val logger = LoggerFactory.getLogger("Camp2Camp") - - val loggingFilter = RequestFilters.Tap { - logger.info("${it.method} ${it.uri}") - } - - val catchAll = Filter { next -> - { - try { - next(it) - } catch (e: Exception) { - val sw = StringWriter() - e.printStackTrace(PrintWriter(sw)) - logger.error(sw.toString()) - Response(Status.INTERNAL_SERVER_ERROR) - } - } - } - - catchAll.then(ServerFilters.GZip()).then(loggingFilter).then(app).asServer(CustomApacheServer(4000)).start() - logger.info("Listening on http://localhost:4000") -} diff --git a/src/main/kotlin/Camp2Camp.kt b/src/main/kotlin/Camp2Camp.kt new file mode 100644 index 0000000..c744670 --- /dev/null +++ b/src/main/kotlin/Camp2Camp.kt @@ -0,0 +1,53 @@ +package be.simplenotes.c2c + +import be.simplenotes.c2c.filters.catchAllFilter +import be.simplenotes.c2c.filters.loggingFilter +import be.simplenotes.c2c.routes.RouteProvider +import org.http4k.contract.contract +import org.http4k.contract.openapi.ApiInfo +import org.http4k.contract.openapi.v3.OpenApi3 +import org.http4k.core.then +import org.http4k.filter.ServerFilters +import org.http4k.format.Jackson +import org.http4k.server.ServerConfig +import org.http4k.server.asServer +import org.koin.core.context.startKoin +import org.slf4j.LoggerFactory +import java.net.InetSocketAddress + +fun main() { + val koin = startKoin { + modules( + serverModule, + routes, + clientModule, + apiModule, + utilsModule, + pdfModule, + ) + properties(mapOf( + "port" to System.getenv().getOrDefault("PORT", "4000"), + "host" to System.getenv().getOrDefault("HOST", "localhost"), + )) + }.koin + + val appRoutes = koin.getAll().map(RouteProvider::route) + val app = contract { + renderer = OpenApi3(ApiInfo("Camp2Camp", "1.0-SNAPSHOT"), Jackson) + descriptionPath = "/api/swagger.json" + routes.all.addAll(appRoutes) + } + + val logger = LoggerFactory.getLogger("Camp2Camp") + val server = koin.get() + val address = koin.get() + + catchAllFilter(logger) + .then(ServerFilters.GZip()) + .then(loggingFilter(logger)) + .then(app) + .asServer(server) + .start() + + logger.info("Listening on http://${address.hostName}:${address.port}") +} diff --git a/src/main/kotlin/Modules.kt b/src/main/kotlin/Modules.kt new file mode 100644 index 0000000..0489ceb --- /dev/null +++ b/src/main/kotlin/Modules.kt @@ -0,0 +1,53 @@ +package be.simplenotes.c2c + +import be.simplenotes.c2c.api.Api +import be.simplenotes.c2c.api.ApiUrls +import be.simplenotes.c2c.api.ImageDownloader +import be.simplenotes.c2c.pdf.PdfCreator +import be.simplenotes.c2c.routes.* +import be.simplenotes.c2c.server.CustomApacheServer +import be.simplenotes.c2c.templates.PebbleEngine +import com.mitchellbosecke.pebble.PebbleEngine +import org.apache.hc.client5.http.impl.DefaultRedirectStrategy +import org.apache.hc.client5.http.impl.classic.HttpClientBuilder +import org.http4k.client.ApacheClient +import org.http4k.format.Jackson +import org.http4k.server.ServerConfig +import org.koin.core.qualifier.named +import org.koin.dsl.bind +import org.koin.dsl.module +import java.net.InetSocketAddress + +val routes = module { + single { SearchRouteProvider(get()) } bind RouteProvider::class + single { RouteRouteProvider(get()) } bind RouteProvider::class + single { PdfRouteProvider(get(), get(), get()) } bind RouteProvider::class + single { IndexRouteProvider(get()) } bind RouteProvider::class +} + +val serverModule = module { + single { InetSocketAddress(getProperty("host"), getProperty("port").toInt()) } + single { CustomApacheServer(get()) } +} + +val clientModule = module { + single { ApacheClient(get()) } + single { HttpClientBuilder.create().setRedirectStrategy(DefaultRedirectStrategy()).build() } +} + +val apiModule = module { + single { ApiUrls(get()) } + single { Api(get(), get()) } + single { ImageDownloader(get()) } +} + +val utilsModule = module { + single { Jackson.mapper } + single { PebbleEngine(cache = false, prefix = "templates/") } +} + +val pdfModule = module { + val pdf = named("pdf") + single { PdfCreator(get(pdf)) } + single(pdf) { get().getTemplate("pdf") } +} diff --git a/src/main/kotlin/api/ImageDownloader.kt b/src/main/kotlin/api/ImageDownloader.kt new file mode 100644 index 0000000..74bd80c --- /dev/null +++ b/src/main/kotlin/api/ImageDownloader.kt @@ -0,0 +1,24 @@ +package be.simplenotes.c2c.api + +import org.http4k.core.HttpHandler +import org.http4k.core.Method +import org.http4k.core.Request +import org.slf4j.LoggerFactory +import java.util.stream.Collectors + +class ImageDownloader(private val client: HttpHandler) { + private val logger = LoggerFactory.getLogger(javaClass) + + fun download(images: List): Map = + images.map(Image::medium) + .parallelStream() + .collect(Collectors.toConcurrentMap( + { it }, + { + val res = client(Request(Method.GET, it)) + logger.info("GET ${res.status} $it") + if (res.status.successful) res.body.stream.readAllBytes() + else null + } + )) +} diff --git a/src/main/kotlin/filters/Filters.kt b/src/main/kotlin/filters/Filters.kt new file mode 100644 index 0000000..6e59d5b --- /dev/null +++ b/src/main/kotlin/filters/Filters.kt @@ -0,0 +1,26 @@ +package be.simplenotes.c2c.filters + +import org.http4k.core.Filter +import org.http4k.core.Response +import org.http4k.core.Status +import org.http4k.filter.RequestFilters +import org.slf4j.Logger +import java.io.PrintWriter +import java.io.StringWriter + +fun loggingFilter(logger: Logger) = RequestFilters.Tap { + logger.info("${it.method} ${it.uri}") +} + +fun catchAllFilter(logger: Logger) = Filter { next -> + { + try { + next(it) + } catch (e: Exception) { + val sw = StringWriter() + e.printStackTrace(PrintWriter(sw)) + logger.error(sw.toString()) + Response(Status.INTERNAL_SERVER_ERROR) + } + } +} diff --git a/src/main/kotlin/pdf/PdfCreator.kt b/src/main/kotlin/pdf/PdfCreator.kt index a92d260..1cd7417 100644 --- a/src/main/kotlin/pdf/PdfCreator.kt +++ b/src/main/kotlin/pdf/PdfCreator.kt @@ -2,59 +2,19 @@ package be.simplenotes.c2c.pdf import be.simplenotes.c2c.api.Route import be.simplenotes.c2c.templates.render -import com.github.benmanes.caffeine.cache.Caffeine -import com.mitchellbosecke.pebble.PebbleEngine -import com.mitchellbosecke.pebble.loader.ClasspathLoader -import com.openhtmltopdf.extend.FSStream +import com.mitchellbosecke.pebble.template.PebbleTemplate import com.openhtmltopdf.extend.FSStreamFactory import com.openhtmltopdf.pdfboxout.PdfRendererBuilder import com.openhtmltopdf.util.XRLog -import org.http4k.core.HttpHandler -import org.http4k.core.Method -import org.http4k.core.Request import org.jsoup.Jsoup import org.jsoup.helper.W3CDom -import org.slf4j.LoggerFactory import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream -import java.io.InputStream -import java.util.concurrent.TimeUnit -class StreamFactory(private val client: HttpHandler) : FSStreamFactory { - private val logger = LoggerFactory.getLogger(javaClass) +class PdfCreator(private val template: PebbleTemplate) { - private val imgCache = Caffeine.newBuilder() - .maximumSize(15) - .expireAfterWrite(15, TimeUnit.MINUTES) - .build() - - override fun getUrl(url: String) = object : FSStream { - override fun getStream(): InputStream? { - val bytes = imgCache.get(url) { - logger.info("Downloading $url") - val res = client(Request(Method.GET, url)) - if (res.status.successful) res.body.stream.readAllBytes() - else null - } - return bytes?.inputStream() - } - - override fun getReader() = null - } -} - -class PdfCreator(private val streamFactory: StreamFactory) { - private val engine = PebbleEngine - .Builder() - .loader(ClasspathLoader().apply { - prefix = "templates/" - suffix = ".twig" - }) - .cacheActive(true) - .build() - - fun create(route: Route): ByteArrayInputStream { - val html = engine.render("index", mapOf("route" to route)) + fun create(route: Route, streamFactory: FSStreamFactory): ByteArrayInputStream { + val html = template.render(mapOf("route" to route)) XRLog.setLoggingEnabled(false) diff --git a/src/main/kotlin/pdf/PreloadedStreamFactory.kt b/src/main/kotlin/pdf/PreloadedStreamFactory.kt new file mode 100644 index 0000000..8ae0f50 --- /dev/null +++ b/src/main/kotlin/pdf/PreloadedStreamFactory.kt @@ -0,0 +1,12 @@ +package be.simplenotes.c2c.pdf + +import com.openhtmltopdf.extend.FSStream +import com.openhtmltopdf.extend.FSStreamFactory +import java.io.InputStream + +class PreloadedStreamFactory(private val images: Map) : FSStreamFactory { + override fun getUrl(url: String) = object : FSStream { + override fun getStream(): InputStream? = images[url]?.inputStream() + override fun getReader() = null + } +} diff --git a/src/main/kotlin/routes/IndexRoute.kt b/src/main/kotlin/routes/IndexRouteProvider.kt similarity index 55% rename from src/main/kotlin/routes/IndexRoute.kt rename to src/main/kotlin/routes/IndexRouteProvider.kt index 7944157..378a425 100644 --- a/src/main/kotlin/routes/IndexRoute.kt +++ b/src/main/kotlin/routes/IndexRouteProvider.kt @@ -4,20 +4,18 @@ import be.simplenotes.c2c.templates.render import com.mitchellbosecke.pebble.PebbleEngine import org.http4k.contract.ContractRoute import org.http4k.contract.meta -import org.http4k.core.HttpHandler -import org.http4k.core.Method -import org.http4k.core.Response -import org.http4k.core.Status +import org.http4k.core.* -class IndexRoute(private val engine: PebbleEngine) { +class IndexRouteProvider(private val engine: PebbleEngine) : RouteProvider { - fun route(): ContractRoute { + override fun route(): ContractRoute { val spec = "/" meta { summary = "Redoc ui" + produces += ContentType.TEXT_HTML } bindContract Method.GET val route: HttpHandler = { - Response(Status.OK).body(engine.render("index")) + Response(Status.OK).body(engine.render("index")).header("Content-Type", "text/html; charset=utf-8") } return spec to route diff --git a/src/main/kotlin/routes/PdfRoute.kt b/src/main/kotlin/routes/PdfRoute.kt deleted file mode 100644 index 418e260..0000000 --- a/src/main/kotlin/routes/PdfRoute.kt +++ /dev/null @@ -1,38 +0,0 @@ -package be.simplenotes.c2c.routes - -import be.simplenotes.c2c.api.Api -import be.simplenotes.c2c.pdf.PdfCreator -import org.http4k.contract.ContractRoute -import org.http4k.contract.div -import org.http4k.contract.meta -import org.http4k.core.HttpHandler -import org.http4k.core.Method -import org.http4k.core.Response -import org.http4k.core.Status -import org.http4k.lens.Path - -// TODO: rename.. -class PdfRoute(private val api: Api, private val pdfCreator: PdfCreator) { - - fun route(): ContractRoute { - val spec = "/route" / Path.of("id", "Route's ID") / "pdf" meta { - summary = "Generate pdf from routes" - } bindContract Method.GET - - fun route(id: String, pdf: String): HttpHandler = { - when (val route = api.getRoute(id)) { - null -> Response(Status.NOT_FOUND) - else -> { - val bytes = pdfCreator.create(route) - Response(Status.OK) - .body(bytes) - .header("Content-Type", "application/pdf") - .header("Content-Disposition", "attachment; filename=\"out.pdf\"") - } - } - } - - return spec to ::route - } - -} diff --git a/src/main/kotlin/routes/PdfRouteProvider.kt b/src/main/kotlin/routes/PdfRouteProvider.kt new file mode 100644 index 0000000..1a60773 --- /dev/null +++ b/src/main/kotlin/routes/PdfRouteProvider.kt @@ -0,0 +1,39 @@ +package be.simplenotes.c2c.routes + +import be.simplenotes.c2c.api.Api +import be.simplenotes.c2c.api.ImageDownloader +import be.simplenotes.c2c.pdf.PdfCreator +import be.simplenotes.c2c.pdf.PreloadedStreamFactory +import org.http4k.contract.ContractRoute +import org.http4k.contract.div +import org.http4k.contract.meta +import org.http4k.core.* +import org.http4k.lens.Path + +class PdfRouteProvider( + private val api: Api, + private val pdfCreator: PdfCreator, + private val imageDownloader: ImageDownloader, +) : RouteProvider { + + override fun route(): ContractRoute { + val spec = "/route" / Path.of("id", "Route's ID") / "pdf" meta { + summary = "Generate pdf from routes" + produces += ContentType("application/pdf") + } bindContract Method.GET + + fun route(id: String, pdf: String): HttpHandler = { + api.getRoute(id)?.let { + val imageMap = imageDownloader.download(it.associations.images) + + Response(Status.OK) + .body(pdfCreator.create(it, PreloadedStreamFactory(imageMap))) + .header("Content-Type", "application/pdf") + .header("Content-Disposition", "attachment; filename=\"out.pdf\"") + } ?: Response(Status.NOT_FOUND) + } + + return spec to ::route + } + +} diff --git a/src/main/kotlin/routes/RouteProvider.kt b/src/main/kotlin/routes/RouteProvider.kt new file mode 100644 index 0000000..39cbc3a --- /dev/null +++ b/src/main/kotlin/routes/RouteProvider.kt @@ -0,0 +1,7 @@ +package be.simplenotes.c2c.routes + +import org.http4k.contract.ContractRoute + +interface RouteProvider { + fun route(): ContractRoute +} diff --git a/src/main/kotlin/routes/RouteRoute.kt b/src/main/kotlin/routes/RouteRouteProvider.kt similarity index 82% rename from src/main/kotlin/routes/RouteRoute.kt rename to src/main/kotlin/routes/RouteRouteProvider.kt index 3350460..6f0b0f1 100644 --- a/src/main/kotlin/routes/RouteRoute.kt +++ b/src/main/kotlin/routes/RouteRouteProvider.kt @@ -10,13 +10,14 @@ import org.http4k.format.Jackson.auto import org.http4k.lens.Path // TODO: rename.. -class RouteRoute(private val api: Api) { +class RouteRouteProvider(private val api: Api) : RouteProvider { - fun routeRoute(): ContractRoute { + override fun route(): ContractRoute { val responseLens = Body.auto("Route").toLens() val spec = "/route" / Path.of("id", "Route's ID") meta { summary = "Route details" + produces += ContentType.APPLICATION_JSON } bindContract Method.GET fun route(id: String): HttpHandler = { diff --git a/src/main/kotlin/routes/SearchRoute.kt b/src/main/kotlin/routes/SearchRouteProvider.kt similarity index 83% rename from src/main/kotlin/routes/SearchRoute.kt rename to src/main/kotlin/routes/SearchRouteProvider.kt index 71b530a..1492dd1 100644 --- a/src/main/kotlin/routes/SearchRoute.kt +++ b/src/main/kotlin/routes/SearchRouteProvider.kt @@ -5,21 +5,22 @@ import be.simplenotes.c2c.api.Api import be.simplenotes.c2c.api.RouteResult import be.simplenotes.c2c.api.SearchResults import org.http4k.contract.ContractRoute -import org.http4k.contract.Tag +import org.http4k.contract.RequestMeta import org.http4k.contract.meta import org.http4k.core.* import org.http4k.format.Jackson.auto import org.http4k.lens.Query -class SearchRoute(private val api: Api) { +class SearchRouteProvider(private val api: Api) : RouteProvider { - fun searchRoute(): ContractRoute { + override fun route(): ContractRoute { val query = Query.required("q", "Search terms") val responseLens = Body.auto("Search results").toLens() val spec = "/search" meta { summary = "Search routes and waypoints" queries += query + receiving(RequestMeta(Request(Method.GET, "/search").query("q", "tour de bavon"))) returning(Status.OK, responseLens to SearchResults(listOf(RouteResult( id = "57103", title = "Tour de Bavon : Thor", diff --git a/src/main/kotlin/CustomApacheServer.kt b/src/main/kotlin/server/CustomApacheServer.kt similarity index 76% rename from src/main/kotlin/CustomApacheServer.kt rename to src/main/kotlin/server/CustomApacheServer.kt index 400c8b4..8b36370 100644 --- a/src/main/kotlin/CustomApacheServer.kt +++ b/src/main/kotlin/server/CustomApacheServer.kt @@ -1,4 +1,4 @@ -package be.simplenotes.c2c +package be.simplenotes.c2c.server import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap import org.apache.hc.core5.http.io.SocketConfig @@ -6,13 +6,16 @@ import org.http4k.core.HttpHandler import org.http4k.server.Http4kRequestHandler import org.http4k.server.Http4kServer import org.http4k.server.ServerConfig +import java.net.InetSocketAddress + +class CustomApacheServer(private val socketAddress: InetSocketAddress) : ServerConfig { -class CustomApacheServer(val port: Int) : ServerConfig { override fun toServer(httpHandler: HttpHandler): Http4kServer = object : Http4kServer { val handler = Http4kRequestHandler(httpHandler) val server = ServerBootstrap.bootstrap() - .setListenerPort(port) + .setListenerPort(socketAddress.port) + .setLocalAddress(socketAddress.address) .setSocketConfig(SocketConfig.custom() .setTcpNoDelay(true) .setSoKeepAlive(true) @@ -28,6 +31,6 @@ class CustomApacheServer(val port: Int) : ServerConfig { override fun stop() = apply { server.stop() } - override fun port(): Int = port + override fun port(): Int = socketAddress.port } } diff --git a/src/main/kotlin/templates/Templates.kt b/src/main/kotlin/templates/Templates.kt index d8edcd8..a2cf8af 100644 --- a/src/main/kotlin/templates/Templates.kt +++ b/src/main/kotlin/templates/Templates.kt @@ -1,11 +1,21 @@ package be.simplenotes.c2c.templates import com.mitchellbosecke.pebble.PebbleEngine +import com.mitchellbosecke.pebble.loader.ClasspathLoader +import com.mitchellbosecke.pebble.template.PebbleTemplate import java.io.StringWriter -fun PebbleEngine.render(name: String, args: Map = mapOf()): String { - val template = getTemplate(name) +fun PebbleEngine.render(name: String, args: Map = mapOf()) = getTemplate(name).render(args) + +fun PebbleTemplate.render(args: Map = mapOf()): String { val writer = StringWriter() - template.evaluate(writer, args) + evaluate(writer, args) return writer.toString() } + +@Suppress("FunctionName") +fun PebbleEngine(cache: Boolean, prefix: String? = null, suffix: String? = ".twig"): PebbleEngine = + PebbleEngine.Builder().loader(ClasspathLoader().also { loader -> + prefix?.let { loader.prefix = it } + suffix?.let { loader.suffix = it } + }).cacheActive(cache).build() diff --git a/src/main/resources/templates/index.twig b/src/main/resources/templates/index.twig index 88bf5a0..c10fd97 100644 --- a/src/main/resources/templates/index.twig +++ b/src/main/resources/templates/index.twig @@ -1,10 +1,19 @@ -

{{ route.title }}

-

{{ route.summary }}

-

{{ route.description }}

-{% for img in route.associations.images %} -
- {{ img.title }} -
{{ img.title }}
-
-{% endfor %} + + + + ReDoc + + + + + + + + + + diff --git a/src/main/resources/templates/pdf.twig b/src/main/resources/templates/pdf.twig new file mode 100644 index 0000000..88bf5a0 --- /dev/null +++ b/src/main/resources/templates/pdf.twig @@ -0,0 +1,10 @@ +

{{ route.title }}

+

{{ route.summary }}

+

{{ route.description }}

+{% for img in route.associations.images %} +
+ {{ img.title }} +
{{ img.title }}
+
+{% endfor %} diff --git a/src/main/resources/views/index.twig b/src/main/resources/views/index.twig deleted file mode 100644 index c10fd97..0000000 --- a/src/main/resources/views/index.twig +++ /dev/null @@ -1,19 +0,0 @@ - - - - ReDoc - - - - - - - - - -