Compare commits
12 Commits
8e6a14b3e0
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 8bcc50ea1b | |||
| d063ba1c25 | |||
| 68fdac4d64 | |||
| aa585358be | |||
| d84770970d | |||
| fe03d64122 | |||
| 13e269842b | |||
| d4635f82ae | |||
| 009ddd3d16 | |||
| ed4d9264eb | |||
| bb5e955318 | |||
| 6c0d17299e |
+7
-13
@@ -16,6 +16,11 @@ display = "Java Version"
|
|||||||
default = "1.4.10"
|
default = "1.4.10"
|
||||||
display = "Kotlin Version"
|
display = "Kotlin Version"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
|
||||||
|
[features.ktlint]
|
||||||
|
default = true
|
||||||
|
|
||||||
[versions]
|
[versions]
|
||||||
http4k = "3.265.0"
|
http4k = "3.265.0"
|
||||||
pebble = "3.1.4"
|
pebble = "3.1.4"
|
||||||
@@ -27,7 +32,7 @@ flyway = "7.0.0"
|
|||||||
hikaricp = "3.4.5"
|
hikaricp = "3.4.5"
|
||||||
ktorm = "3.0.0"
|
ktorm = "3.0.0"
|
||||||
junit = "5.7.0"
|
junit = "5.7.0"
|
||||||
mokk = "1.10.0"
|
mockk = "1.10.0"
|
||||||
hamkrest = "1.7.0.3"
|
hamkrest = "1.7.0.3"
|
||||||
assertj = "3.17.2"
|
assertj = "3.17.2"
|
||||||
kodein-di = "7.1.0"
|
kodein-di = "7.1.0"
|
||||||
@@ -54,7 +59,6 @@ default = true
|
|||||||
|
|
||||||
[dependencies.http4k-server-jetty]
|
[dependencies.http4k-server-jetty]
|
||||||
groupId = "org.http4k"
|
groupId = "org.http4k"
|
||||||
artifactId = "http4k-server-jetty"
|
|
||||||
version = "http4k"
|
version = "http4k"
|
||||||
category = "http4k"
|
category = "http4k"
|
||||||
default = true
|
default = true
|
||||||
@@ -62,43 +66,36 @@ logger = "org.eclipse.jetty"
|
|||||||
|
|
||||||
[dependencies.http4k-server-apache]
|
[dependencies.http4k-server-apache]
|
||||||
groupId = "org.http4k"
|
groupId = "org.http4k"
|
||||||
artifactId = "http4k-server-apache"
|
|
||||||
version = "http4k"
|
version = "http4k"
|
||||||
category = "http4k"
|
category = "http4k"
|
||||||
|
|
||||||
[dependencies.http4k-client-apache]
|
[dependencies.http4k-client-apache]
|
||||||
groupId = "org.http4k"
|
groupId = "org.http4k"
|
||||||
artifactId = "http4k-client-apache"
|
|
||||||
version = "http4k"
|
version = "http4k"
|
||||||
category = "http4k"
|
category = "http4k"
|
||||||
|
|
||||||
[dependencies.http4k-format-jackson]
|
[dependencies.http4k-format-jackson]
|
||||||
groupId = "org.http4k"
|
groupId = "org.http4k"
|
||||||
artifactId = "http4k-format-jackson"
|
|
||||||
version = "http4k"
|
version = "http4k"
|
||||||
category = "http4k"
|
category = "http4k"
|
||||||
|
|
||||||
[dependencies.http4k-format-kotlinx-serialization]
|
[dependencies.http4k-format-kotlinx-serialization]
|
||||||
groupId = "org.http4k"
|
groupId = "org.http4k"
|
||||||
artifactId = "http4k-format-kotlinx-serialization"
|
|
||||||
version = "http4k"
|
version = "http4k"
|
||||||
category = "http4k"
|
category = "http4k"
|
||||||
|
|
||||||
[dependencies.http4k-contract]
|
[dependencies.http4k-contract]
|
||||||
groupId = "org.http4k"
|
groupId = "org.http4k"
|
||||||
artifactId = "http4k-contract"
|
|
||||||
version = "http4k"
|
version = "http4k"
|
||||||
category = "http4k"
|
category = "http4k"
|
||||||
|
|
||||||
[dependencies.pebble]
|
[dependencies.pebble]
|
||||||
groupId = "io.pebbletemplates"
|
groupId = "io.pebbletemplates"
|
||||||
artifactId = "pebble"
|
|
||||||
default = true
|
default = true
|
||||||
logger = "com.mitchellbosecke.pebble"
|
logger = "com.mitchellbosecke.pebble"
|
||||||
|
|
||||||
[dependencies.caffeine]
|
[dependencies.caffeine]
|
||||||
groupId = "com.github.ben-manes.caffeine"
|
groupId = "com.github.ben-manes.caffeine"
|
||||||
artifactId = "caffeine"
|
|
||||||
|
|
||||||
[dependencies.logback]
|
[dependencies.logback]
|
||||||
groupId = "ch.qos.logback"
|
groupId = "ch.qos.logback"
|
||||||
@@ -112,7 +109,6 @@ category = "database"
|
|||||||
|
|
||||||
[dependencies.h2]
|
[dependencies.h2]
|
||||||
groupId = "com.h2database"
|
groupId = "com.h2database"
|
||||||
artifactId = "h2"
|
|
||||||
category = "database"
|
category = "database"
|
||||||
|
|
||||||
[dependencies.flyway]
|
[dependencies.flyway]
|
||||||
@@ -154,7 +150,7 @@ scope = "test"
|
|||||||
category = "test"
|
category = "test"
|
||||||
default = true
|
default = true
|
||||||
|
|
||||||
[dependencies.mokk]
|
[dependencies.mockk]
|
||||||
groupId = "io.mockk"
|
groupId = "io.mockk"
|
||||||
artifactId = "mockk"
|
artifactId = "mockk"
|
||||||
scope = "test"
|
scope = "test"
|
||||||
@@ -162,7 +158,6 @@ category = "test"
|
|||||||
|
|
||||||
[dependencies.hamkrest]
|
[dependencies.hamkrest]
|
||||||
groupId = "com.natpryce"
|
groupId = "com.natpryce"
|
||||||
artifactId = "hamkrest"
|
|
||||||
scope = "test"
|
scope = "test"
|
||||||
category = "test"
|
category = "test"
|
||||||
|
|
||||||
@@ -198,5 +193,4 @@ category = "serialization"
|
|||||||
|
|
||||||
[dependencies.arrow-core]
|
[dependencies.arrow-core]
|
||||||
groupId = "io.arrow-kt"
|
groupId = "io.arrow-kt"
|
||||||
artifactId = "arrow-core"
|
|
||||||
repository = "arrow"
|
repository = "arrow"
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.target>11</maven.compiler.target>
|
<maven.compiler.target>14</maven.compiler.target>
|
||||||
<maven.compiler.source>11</maven.compiler.source>
|
<maven.compiler.source>14</maven.compiler.source>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<kotlin.version>1.4.10</kotlin.version>
|
<kotlin.version>1.4.10</kotlin.version>
|
||||||
<kotlin.code.style>official</kotlin.code.style>
|
<kotlin.code.style>official</kotlin.code.style>
|
||||||
@@ -57,6 +57,25 @@
|
|||||||
<artifactId>slf4j-simple</artifactId>
|
<artifactId>slf4j-simple</artifactId>
|
||||||
<version>1.7.30</version>
|
<version>1.7.30</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<version>5.7.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-params</artifactId>
|
||||||
|
<version>5.7.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.assertj</groupId>
|
||||||
|
<artifactId>assertj-core</artifactId>
|
||||||
|
<version>3.17.2</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
@@ -129,7 +148,7 @@
|
|||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
<configuration>
|
<configuration>
|
||||||
<jvmTarget>11</jvmTarget>
|
<jvmTarget>14</jvmTarget>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
@@ -137,6 +156,50 @@
|
|||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.8.1</version>
|
<version>3.8.1</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-antrun-plugin</artifactId>
|
||||||
|
<version>1.7</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>ktlint</id>
|
||||||
|
<phase>verify</phase>
|
||||||
|
<configuration>
|
||||||
|
<target name="ktlint">
|
||||||
|
<java taskname="ktlint" dir="${basedir}" fork="true" failonerror="true"
|
||||||
|
classname="com.pinterest.ktlint.Main" classpathref="maven.plugin.classpath">
|
||||||
|
<arg value="src/**/*.kt"/>
|
||||||
|
</java>
|
||||||
|
</target>
|
||||||
|
</configuration>
|
||||||
|
<goals>
|
||||||
|
<goal>run</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>ktlint-format</id>
|
||||||
|
<configuration>
|
||||||
|
<target name="ktlint">
|
||||||
|
<java taskname="ktlint" dir="${basedir}" fork="true" failonerror="true"
|
||||||
|
classname="com.pinterest.ktlint.Main" classpathref="maven.plugin.classpath">
|
||||||
|
<arg value="-F"/>
|
||||||
|
<arg value="src/**/*.kt"/>
|
||||||
|
</java>
|
||||||
|
</target>
|
||||||
|
</configuration>
|
||||||
|
<goals>
|
||||||
|
<goal>run</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.pinterest</groupId>
|
||||||
|
<artifactId>ktlint</artifactId>
|
||||||
|
<version>0.39.0</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
package starter
|
package starter
|
||||||
|
|
||||||
import org.koin.core.context.startKoin
|
import org.koin.core.context.startKoin
|
||||||
import starter.modules.mainModule
|
import org.koin.core.context.unloadKoinModules
|
||||||
import starter.modules.pebbleModule
|
import starter.modules.*
|
||||||
import starter.modules.routesModule
|
|
||||||
import starter.modules.templateModule
|
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
startKoin {
|
startKoin {
|
||||||
modules(mainModule, pebbleModule, templateModule, routesModule)
|
modules(mainModule, pebbleModule, templateModule, configModule, routesModule)
|
||||||
}
|
}
|
||||||
|
unloadKoinModules(configModule)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,25 +12,27 @@ data class Dependency(
|
|||||||
val name: String,
|
val name: String,
|
||||||
val groupId: String,
|
val groupId: String,
|
||||||
val artifactId: String,
|
val artifactId: String,
|
||||||
val version: String,
|
val version: Version,
|
||||||
val default: Boolean,
|
val default: Boolean,
|
||||||
val category: Category,
|
val category: Category,
|
||||||
val scope: Scope,
|
val scope: Scope,
|
||||||
val logger: String?,
|
val logger: String?,
|
||||||
val repository: String?,
|
val repository: Repository?,
|
||||||
val versionKey: String,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Repository(val name: String, val url: String)
|
data class Repository(val name: String, val url: String)
|
||||||
|
|
||||||
data class Input(val name: String, val display: String, val value: String? = null)
|
data class Input(val name: String, val display: String, val value: String? = null)
|
||||||
|
|
||||||
|
data class Feature(val name: String, val value: Boolean = false)
|
||||||
|
|
||||||
data class Project(
|
data class Project(
|
||||||
val name: String,
|
val name: String,
|
||||||
val basePackage: String,
|
val basePackage: String,
|
||||||
val inputs: List<Input>,
|
val inputs: List<Input>,
|
||||||
|
val features: List<Feature>,
|
||||||
val dependencies: List<Dependency>,
|
val dependencies: List<Dependency>,
|
||||||
val repositories: List<Repository>,
|
val repositories: Set<Repository>,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Version(val name: String, val value: String)
|
data class Version(val name: String, val value: String)
|
||||||
|
|||||||
@@ -1,44 +1,52 @@
|
|||||||
package starter.config
|
package starter.config
|
||||||
|
|
||||||
import com.electronwill.nightconfig.core.UnmodifiableConfig
|
import com.electronwill.nightconfig.core.UnmodifiableConfig
|
||||||
import com.electronwill.nightconfig.core.file.FileConfig
|
|
||||||
import starter.*
|
import starter.*
|
||||||
import com.electronwill.nightconfig.core.Config as NightConfig
|
import com.electronwill.nightconfig.core.Config as NightConfig
|
||||||
|
|
||||||
data class StarterConfig(
|
data class StarterConfig(
|
||||||
val dependencies: List<Dependency>,
|
val dependencies: List<Dependency>,
|
||||||
val inputs: List<Input>,
|
val inputs: List<Input>,
|
||||||
val repositories: List<Repository>,
|
val features: List<Feature>,
|
||||||
)
|
)
|
||||||
|
|
||||||
class Config {
|
class Config(private val cfg: NightConfig) {
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
private fun FileConfig.configMap(key: String) = this.get<NightConfig>(key).valueMap() as Map<String, NightConfig>
|
private fun NightConfig.configMap(key: String) = this.get<NightConfig>(key)
|
||||||
|
?.valueMap() as Map<String, NightConfig>?
|
||||||
|
?: emptyMap()
|
||||||
|
|
||||||
fun load(): StarterConfig {
|
fun load(): StarterConfig {
|
||||||
val cfg = FileConfig.of("config.toml")
|
|
||||||
cfg.load()
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val versions = cfg.get<UnmodifiableConfig>("versions").valueMap() as Map<String, String>
|
val versions = cfg.get<UnmodifiableConfig>("versions")
|
||||||
|
?.valueMap() as Map<String, String>?
|
||||||
|
?: emptyMap()
|
||||||
|
|
||||||
|
val repositories = cfg.configMap("repositories")
|
||||||
|
.map { (name, values) ->
|
||||||
|
Repository(name, values["url"])
|
||||||
|
}
|
||||||
|
|
||||||
val dependencies = cfg.configMap("dependencies")
|
val dependencies = cfg.configMap("dependencies")
|
||||||
.map { (name, values) ->
|
.map { (name, values) ->
|
||||||
val versionKey: String = values["version"] ?: name
|
val versionKey: String = values["version"] ?: name
|
||||||
val version = versions[versionKey] ?: error("Missing version for $name")
|
val version = versions[versionKey] ?: error("Missing version for $name")
|
||||||
|
|
||||||
|
val repositoryName: String? = values["repository"]
|
||||||
|
val repo = repositoryName?.let { repoName -> repositories.find { it.name == repoName } }
|
||||||
|
|
||||||
Dependency(
|
Dependency(
|
||||||
name,
|
name = name,
|
||||||
values["groupId"],
|
groupId = values["groupId"],
|
||||||
values["artifactId"],
|
artifactId = values["artifactId"] ?: name,
|
||||||
version,
|
version = Version(versionKey, version),
|
||||||
values.getOrElse("default", false),
|
default = values.getOrElse("default", false),
|
||||||
values.getEnumOrElse("category", Category.Other),
|
category = values.getEnumOrElse("category", Category.Other),
|
||||||
values.getEnumOrElse("scope", Scope.Compile),
|
scope = values.getEnumOrElse("scope", Scope.Compile),
|
||||||
values["logger"],
|
logger = values["logger"],
|
||||||
values["repository"],
|
repository = repo,
|
||||||
versionKey
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,11 +55,11 @@ class Config {
|
|||||||
Input(name, values["display"], values["default"])
|
Input(name, values["display"], values["default"])
|
||||||
}
|
}
|
||||||
|
|
||||||
val repositories = cfg.configMap("repositories")
|
val features = cfg.configMap("features")
|
||||||
.map { (name, values) ->
|
.map { (name, values) ->
|
||||||
Repository(name, values["url"])
|
Feature(name, values["default"] ?: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
return StarterConfig(dependencies, inputs, repositories)
|
return StarterConfig(dependencies, inputs, features)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package starter.modules
|
||||||
|
|
||||||
|
import com.electronwill.nightconfig.core.file.FileConfig
|
||||||
|
import org.koin.dsl.module
|
||||||
|
import starter.config.Config
|
||||||
|
import com.electronwill.nightconfig.core.Config as NightConfig
|
||||||
|
|
||||||
|
val configModule = module {
|
||||||
|
single<NightConfig> { FileConfig.of("config.toml").apply { load() } }
|
||||||
|
single { Config(get()).load() }
|
||||||
|
}
|
||||||
@@ -8,7 +8,6 @@ import org.koin.dsl.onClose
|
|||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import starter.ProjectZip
|
import starter.ProjectZip
|
||||||
import starter.config.Config
|
|
||||||
import starter.views.Views
|
import starter.views.Views
|
||||||
|
|
||||||
val mainModule = module {
|
val mainModule = module {
|
||||||
@@ -17,7 +16,6 @@ val mainModule = module {
|
|||||||
get<RoutingHttpHandler>().asServer(SunHttp(7000)).start()
|
get<RoutingHttpHandler>().asServer(SunHttp(7000)).start()
|
||||||
} onClose { it?.stop() }
|
} onClose { it?.stop() }
|
||||||
|
|
||||||
single { Config().load() }
|
|
||||||
single { LoggerFactory.getLogger("Starter") }
|
single { LoggerFactory.getLogger("Starter") }
|
||||||
single { Views(get(), get()) }
|
single { Views(get(), get()) }
|
||||||
single { ProjectZip(getAll()) }
|
single { ProjectZip(getAll()) }
|
||||||
|
|||||||
@@ -1,57 +1,23 @@
|
|||||||
package starter.modules
|
package starter.modules
|
||||||
|
|
||||||
import com.mitchellbosecke.pebble.extension.escaper.SafeString
|
|
||||||
import com.mitchellbosecke.pebble.template.EvaluationContext
|
|
||||||
import com.mitchellbosecke.pebble.template.PebbleTemplate
|
|
||||||
import org.intellij.lang.annotations.Language
|
|
||||||
import org.koin.dsl.bind
|
import org.koin.dsl.bind
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
import starter.Dependency
|
import starter.pebble.DepAsXmlPebbleFunction
|
||||||
|
import starter.pebble.HasFeatureFilter
|
||||||
import starter.utils.PebbleEngineBuilder
|
import starter.utils.PebbleEngineBuilder
|
||||||
import starter.utils.PebbleEngineBuilder.CacheType.ConcurrentMap
|
import starter.utils.PebbleEngineBuilder.CacheType.ConcurrentMap
|
||||||
|
import starter.utils.PebbleFilter
|
||||||
import starter.utils.PebbleFunction
|
import starter.utils.PebbleFunction
|
||||||
|
|
||||||
class DepAsXmlPebbleFunction : PebbleFunction {
|
|
||||||
override val name = "depAsXml"
|
|
||||||
override fun getArgumentNames() = listOf("dependency")
|
|
||||||
override fun execute(args: Map<String, Any>, self: PebbleTemplate, context: EvaluationContext, lineNumber: Int): Any {
|
|
||||||
val dep = args["dependency"] as Dependency
|
|
||||||
|
|
||||||
fun startTag(name: String): String {
|
|
||||||
@Language("html") @Suppress("UnnecessaryVariable")
|
|
||||||
val result = """<span class="text-gray-700"><</span><span class="text-red-700">$name</span><span class="text-gray-700">></span>"""
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
fun endTag(name: String): String {
|
|
||||||
@Language("html") @Suppress("UnnecessaryVariable")
|
|
||||||
val result = """<span class="text-gray-700"></</span><span class="text-red-700">$name</span><span class="text-gray-700">></span>"""
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
fun tag(name: String, content: String): String {
|
|
||||||
return """${startTag(name)}$content${endTag(name)}"""
|
|
||||||
}
|
|
||||||
|
|
||||||
val result = """
|
|
||||||
|${startTag("dependency")}
|
|
||||||
| ${tag("groupId", dep.groupId)}
|
|
||||||
| ${tag("artifactId", dep.artifactId)}
|
|
||||||
| ${tag("version", dep.version)}
|
|
||||||
|${endTag("dependency")}
|
|
||||||
""".trimMargin()
|
|
||||||
|
|
||||||
return SafeString(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val pebbleModule = module {
|
val pebbleModule = module {
|
||||||
single { DepAsXmlPebbleFunction() } bind PebbleFunction::class
|
single { DepAsXmlPebbleFunction() } bind PebbleFunction::class
|
||||||
|
single { HasFeatureFilter() } bind PebbleFilter::class
|
||||||
|
|
||||||
single {
|
single {
|
||||||
PebbleEngineBuilder {
|
PebbleEngineBuilder {
|
||||||
cache = ConcurrentMap
|
cache = ConcurrentMap
|
||||||
functions(getAll())
|
functions(getAll())
|
||||||
|
filters(getAll())
|
||||||
classPath { suffix = ".twig" }
|
classPath { suffix = ".twig" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,15 +11,19 @@ import starter.routes.IndexRouteSupplier
|
|||||||
import starter.routes.RouteSupplier
|
import starter.routes.RouteSupplier
|
||||||
import starter.routes.ZipRouteSupplier
|
import starter.routes.ZipRouteSupplier
|
||||||
import starter.routes.toRouter
|
import starter.routes.toRouter
|
||||||
|
import starter.utils.ProjectExtractor
|
||||||
|
import starter.utils.ProjectExtractorImpl
|
||||||
|
|
||||||
val routesModule = module {
|
val routesModule = module {
|
||||||
single { IndexRouteSupplier(get()) } bind RouteSupplier::class
|
single { IndexRouteSupplier(get()) } bind RouteSupplier::class
|
||||||
single { ZipRouteSupplier(get(), get()) } bind RouteSupplier::class
|
single { ZipRouteSupplier(get(), get()) } bind RouteSupplier::class
|
||||||
|
single<ProjectExtractor> { ProjectExtractorImpl(get()) }
|
||||||
single {
|
single {
|
||||||
ServerFilters.CatchAll().then(
|
ServerFilters.CatchAll().then(
|
||||||
routes(
|
routes(
|
||||||
static(ResourceLoader.Classpath("/assets")),
|
static(ResourceLoader.Classpath("/assets")),
|
||||||
getAll<RouteSupplier>().toRouter()
|
getAll<RouteSupplier>().toRouter()
|
||||||
))
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,4 +9,6 @@ val templateModule = module {
|
|||||||
single { MainTemplate(get()) } bind Template::class
|
single { MainTemplate(get()) } bind Template::class
|
||||||
single { LogbackTemplate(get()) } bind Template::class
|
single { LogbackTemplate(get()) } bind Template::class
|
||||||
single { GitignoreTemplate(get()) } bind Template::class
|
single { GitignoreTemplate(get()) } bind Template::class
|
||||||
|
single { JunitTemplate(get()) } bind Template::class
|
||||||
|
single { EditorConfigTemplate(get()) } bind Template::class
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package starter.pebble
|
||||||
|
|
||||||
|
import com.mitchellbosecke.pebble.extension.escaper.SafeString
|
||||||
|
import com.mitchellbosecke.pebble.template.EvaluationContext
|
||||||
|
import com.mitchellbosecke.pebble.template.PebbleTemplate
|
||||||
|
import org.intellij.lang.annotations.Language
|
||||||
|
import starter.Dependency
|
||||||
|
import starter.utils.PebbleFunction
|
||||||
|
|
||||||
|
// We need a custom function since pebble inserts whitespace everywhere
|
||||||
|
class DepAsXmlPebbleFunction : PebbleFunction {
|
||||||
|
override val name = "depAsXml"
|
||||||
|
override fun getArgumentNames() = listOf("dependency")
|
||||||
|
|
||||||
|
override fun execute(
|
||||||
|
args: Map<String, Any>,
|
||||||
|
self: PebbleTemplate,
|
||||||
|
context: EvaluationContext,
|
||||||
|
lineNumber: Int,
|
||||||
|
): SafeString {
|
||||||
|
val dep = args["dependency"] as Dependency
|
||||||
|
|
||||||
|
fun tagName(name: String) = """<span class="text-red-700">$name</span>"""
|
||||||
|
|
||||||
|
fun startTag(name: String): String {
|
||||||
|
@Language("html") @Suppress("UnnecessaryVariable")
|
||||||
|
val result = """<span class="text-gray-700"><</span>""" +
|
||||||
|
"""${tagName(name)}<span class="text-gray-700">></span>"""
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun endTag(name: String): String {
|
||||||
|
@Language("html") @Suppress("UnnecessaryVariable")
|
||||||
|
val result = """<span class="text-gray-700"></</span>""" +
|
||||||
|
"""${tagName(name)}<span class="text-gray-700">></span>"""
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tag(name: String, content: String) = """${startTag(name)}$content${endTag(name)}"""
|
||||||
|
|
||||||
|
val result = """
|
||||||
|
|${startTag("dependency")}
|
||||||
|
| ${tag("groupId", dep.groupId)}
|
||||||
|
| ${tag("artifactId", dep.artifactId)}
|
||||||
|
| ${tag("version", dep.version.value)}
|
||||||
|
|${endTag("dependency")}
|
||||||
|
""".trimMargin()
|
||||||
|
|
||||||
|
return SafeString(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package starter.pebble
|
||||||
|
|
||||||
|
import com.mitchellbosecke.pebble.template.EvaluationContext
|
||||||
|
import com.mitchellbosecke.pebble.template.PebbleTemplate
|
||||||
|
import starter.Feature
|
||||||
|
import starter.utils.PebbleFilter
|
||||||
|
|
||||||
|
class HasFeatureFilter : PebbleFilter {
|
||||||
|
override val name = "hasFeature"
|
||||||
|
|
||||||
|
override fun getArgumentNames() = listOf("name")
|
||||||
|
|
||||||
|
override fun apply(
|
||||||
|
input: Any,
|
||||||
|
args: MutableMap<String, Any>,
|
||||||
|
self: PebbleTemplate,
|
||||||
|
context: EvaluationContext,
|
||||||
|
lineNumber: Int,
|
||||||
|
): Boolean {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val features = input as List<Feature>
|
||||||
|
|
||||||
|
val name = args["name"] as String
|
||||||
|
|
||||||
|
return features.any { it.name == name }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,56 +1,35 @@
|
|||||||
package starter.routes
|
package starter.routes
|
||||||
|
|
||||||
import org.http4k.core.Method
|
import org.http4k.core.Method
|
||||||
|
import org.http4k.core.Request
|
||||||
import org.http4k.core.Response
|
import org.http4k.core.Response
|
||||||
import org.http4k.core.body.form
|
|
||||||
import org.http4k.core.body.formAsMap
|
|
||||||
import org.http4k.core.with
|
import org.http4k.core.with
|
||||||
import org.http4k.routing.bind
|
import org.http4k.routing.bind
|
||||||
import starter.Project
|
|
||||||
import starter.ProjectZip
|
import starter.ProjectZip
|
||||||
import starter.config.StarterConfig
|
|
||||||
import starter.extensions.attachment
|
import starter.extensions.attachment
|
||||||
import starter.extensions.badRequest
|
import starter.extensions.badRequest
|
||||||
import starter.extensions.ok
|
import starter.extensions.ok
|
||||||
|
import starter.utils.ProjectExtractor
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
|
|
||||||
class ZipRouteSupplier(
|
class ZipRouteSupplier(
|
||||||
private val conf: StarterConfig,
|
private val projectExtractor: ProjectExtractor,
|
||||||
private val projectZip: ProjectZip,
|
private val projectZip: ProjectZip,
|
||||||
) : RouteSupplier {
|
) : RouteSupplier {
|
||||||
|
|
||||||
override fun get() = "/" bind Method.POST to { req ->
|
private fun handle(req: Request): Response {
|
||||||
|
val project = projectExtractor(req) ?: return Response.badRequest()
|
||||||
|
|
||||||
val deps = conf.dependencies.filter {
|
val outputStream = projectZip.createZip(project)
|
||||||
req.form(it.name) != null
|
|
||||||
}
|
|
||||||
|
|
||||||
val inputKeys = conf.inputs.map { it.name }
|
return Response.ok().with(
|
||||||
val inputs = req.formAsMap()
|
attachment(
|
||||||
.filter { it.key in inputKeys }
|
value = ByteArrayInputStream(outputStream.toByteArray()),
|
||||||
.map { (name, value) ->
|
name = "${project.name}.zip",
|
||||||
conf.inputs.find { it.name == name }!!.copy(value = value.first())
|
contentType = "application/zip"
|
||||||
}
|
|
||||||
|
|
||||||
val projectName = inputs.find { it.name == "name" }!!.value!!
|
|
||||||
val basePackage = inputs.find { it.name == "basePackage" }!!.value!!
|
|
||||||
|
|
||||||
if (basePackage.contains("/") || basePackage.contains("..")) {
|
|
||||||
Response.badRequest()
|
|
||||||
} else {
|
|
||||||
val repositories = conf.repositories
|
|
||||||
.filter { repo -> repo.name in deps.mapNotNull { it.repository } }
|
|
||||||
|
|
||||||
val project = Project(projectName, basePackage, inputs, deps, repositories)
|
|
||||||
val outputStream = projectZip.createZip(project)
|
|
||||||
|
|
||||||
Response.ok().with(
|
|
||||||
attachment(
|
|
||||||
value = ByteArrayInputStream(outputStream.toByteArray()),
|
|
||||||
name = "$projectName.zip",
|
|
||||||
contentType = "application/zip"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun get() = "/" bind Method.POST to ::handle
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package starter.templates
|
||||||
|
|
||||||
|
import com.mitchellbosecke.pebble.PebbleEngine
|
||||||
|
import starter.Project
|
||||||
|
import starter.utils.render
|
||||||
|
|
||||||
|
class EditorConfigTemplate(private val engine: PebbleEngine) : Template {
|
||||||
|
override fun path(project: Project) =
|
||||||
|
".editorconfig"
|
||||||
|
|
||||||
|
override fun enabled(project: Project) = project.features.any { it.name == "ktlint" }
|
||||||
|
|
||||||
|
override fun render(project: Project) =
|
||||||
|
engine.render("starter/editorconfig/index")
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package starter.templates
|
||||||
|
|
||||||
|
import com.mitchellbosecke.pebble.PebbleEngine
|
||||||
|
import starter.Project
|
||||||
|
import starter.utils.render
|
||||||
|
|
||||||
|
class JunitTemplate(private val engine: PebbleEngine) : Template {
|
||||||
|
override fun path(project: Project) = "src/test/resources/junit-platform.properties"
|
||||||
|
override fun enabled(project: Project) = project.dependencies.any { it.name == "junit" }
|
||||||
|
override fun render(project: Project) = engine.render("starter/junit/index")
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ import starter.utils.render
|
|||||||
|
|
||||||
class MainTemplate(private val engine: PebbleEngine) : Template {
|
class MainTemplate(private val engine: PebbleEngine) : Template {
|
||||||
override fun path(project: Project) =
|
override fun path(project: Project) =
|
||||||
"src/main/kotlin/" + project.basePackage.replace('.', '/') + "/" + project.name.toLowerCase().capitalize() + ".kt"
|
"src/main/kotlin/" + project.name.toLowerCase().capitalize() + ".kt"
|
||||||
|
|
||||||
override fun render(project: Project) =
|
override fun render(project: Project) =
|
||||||
engine.render("starter/main/index", mapOf("basePackage" to project.basePackage))
|
engine.render("starter/main/index", mapOf("basePackage" to project.basePackage))
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package starter.templates
|
|||||||
|
|
||||||
import com.mitchellbosecke.pebble.PebbleEngine
|
import com.mitchellbosecke.pebble.PebbleEngine
|
||||||
import starter.Project
|
import starter.Project
|
||||||
import starter.Version
|
|
||||||
import starter.utils.prettyPrintXml
|
import starter.utils.prettyPrintXml
|
||||||
import starter.utils.render
|
import starter.utils.render
|
||||||
|
|
||||||
@@ -13,8 +12,9 @@ class PomTemplate(private val engine: PebbleEngine) : Template {
|
|||||||
val args: MutableMap<String, Any?> = mutableMapOf(
|
val args: MutableMap<String, Any?> = mutableMapOf(
|
||||||
"dependencies" to project.dependencies.sortedBy { it.scope },
|
"dependencies" to project.dependencies.sortedBy { it.scope },
|
||||||
"repositories" to project.repositories,
|
"repositories" to project.repositories,
|
||||||
"kotlinxSerialization" to project.dependencies.any { it.name == "Kotlinx-serialization" },
|
"kotlinxSerialization" to project.dependencies.any { it.name == "kotlinx-serialization" },
|
||||||
"versions" to project.dependencies.map { Version(name = it.versionKey, value = it.version) }.toSet()
|
"features" to project.features,
|
||||||
|
"versions" to project.dependencies.map { it.version }.toSet()
|
||||||
)
|
)
|
||||||
|
|
||||||
project.inputs.forEach {
|
project.inputs.forEach {
|
||||||
|
|||||||
@@ -64,16 +64,20 @@ class PebbleEngineBuilder {
|
|||||||
PebbleEngine.Builder()
|
PebbleEngine.Builder()
|
||||||
.loader(builder.loader)
|
.loader(builder.loader)
|
||||||
.cacheActive(builder.cache != CacheType.None)
|
.cacheActive(builder.cache != CacheType.None)
|
||||||
.templateCache(when (builder.cache) {
|
.templateCache(
|
||||||
CacheType.None -> NoOpTemplateCache()
|
when (builder.cache) {
|
||||||
CacheType.Caffeine -> CaffeineTemplateCache()
|
CacheType.None -> NoOpTemplateCache()
|
||||||
CacheType.ConcurrentMap -> ConcurrentMapTemplateCache()
|
CacheType.Caffeine -> CaffeineTemplateCache()
|
||||||
})
|
CacheType.ConcurrentMap -> ConcurrentMapTemplateCache()
|
||||||
.tagCache(when (builder.cache) {
|
}
|
||||||
CacheType.None -> NoOpTagCache()
|
)
|
||||||
CacheType.Caffeine -> CaffeineTagCache()
|
.tagCache(
|
||||||
CacheType.ConcurrentMap -> ConcurrentMapTagCache()
|
when (builder.cache) {
|
||||||
})
|
CacheType.None -> NoOpTagCache()
|
||||||
|
CacheType.Caffeine -> CaffeineTagCache()
|
||||||
|
CacheType.ConcurrentMap -> ConcurrentMapTagCache()
|
||||||
|
}
|
||||||
|
)
|
||||||
.extension(object : AbstractExtension() {
|
.extension(object : AbstractExtension() {
|
||||||
override fun getFunctions(): Map<String, Function>? =
|
override fun getFunctions(): Map<String, Function>? =
|
||||||
builder.functions.associateBy { it.name }.ifEmpty { null }
|
builder.functions.associateBy { it.name }.ifEmpty { null }
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package starter.utils
|
||||||
|
|
||||||
|
import org.http4k.core.Request
|
||||||
|
import org.http4k.core.body.form
|
||||||
|
import org.http4k.core.body.formAsMap
|
||||||
|
import starter.Project
|
||||||
|
import starter.config.StarterConfig
|
||||||
|
|
||||||
|
interface ProjectExtractor {
|
||||||
|
operator fun invoke(request: Request): Project?
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProjectExtractorImpl(private val conf: StarterConfig) : ProjectExtractor {
|
||||||
|
override fun invoke(request: Request): Project? {
|
||||||
|
val deps = conf.dependencies.filter {
|
||||||
|
request.form(it.name) != null
|
||||||
|
}
|
||||||
|
|
||||||
|
val formMap = request.formAsMap()
|
||||||
|
|
||||||
|
val inputKeys = conf.inputs.map { it.name }
|
||||||
|
val inputs = formMap
|
||||||
|
.filter { it.key in inputKeys }
|
||||||
|
.map { (name, value) ->
|
||||||
|
conf.inputs.find { it.name == name }!!.copy(value = value.first())
|
||||||
|
}
|
||||||
|
|
||||||
|
val features = formMap
|
||||||
|
.filter { it.key in conf.features.map { it.name } }
|
||||||
|
.map { (name, _) ->
|
||||||
|
conf.features.find { it.name == name }!!.copy(value = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
val projectName = inputs.find { it.name == "name" }?.value ?: return null
|
||||||
|
val basePackage = inputs.find { it.name == "basePackage" }?.value ?: return null
|
||||||
|
|
||||||
|
return if (basePackage.contains("/") || basePackage.contains("..")) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
val repositories = deps.mapNotNull { it.repository }.toSet()
|
||||||
|
Project(projectName, basePackage, inputs, features, deps, repositories)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,8 @@ import starter.utils.render
|
|||||||
class Views(private val engine: PebbleEngine, config: StarterConfig) {
|
class Views(private val engine: PebbleEngine, config: StarterConfig) {
|
||||||
private val args = mapOf(
|
private val args = mapOf(
|
||||||
"dependencies" to config.dependencies.groupBy { it.category }.toSortedMap(),
|
"dependencies" to config.dependencies.groupBy { it.category }.toSortedMap(),
|
||||||
"inputs" to config.inputs
|
"inputs" to config.inputs,
|
||||||
|
"features" to config.features,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun index() = engine.render("views/index", args)
|
fun index() = engine.render("views/index", args)
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.{kt, kts}]
|
||||||
|
indent_size = 4
|
||||||
|
insert_final_newline = true
|
||||||
|
continuation_indent_size=4
|
||||||
|
max_line_length = 120
|
||||||
|
disabled_rules = no-wildcard-imports
|
||||||
|
kotlin_imports_layout = idea
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
junit.jupiter.testinstance.lifecycle.default=per_class
|
||||||
|
junit.jupiter.execution.parallel.enabled=true
|
||||||
|
junit.jupiter.execution.parallel.mode.default=same_thread
|
||||||
|
junit.jupiter.execution.parallel.mode.classes.default=concurrent
|
||||||
@@ -2,13 +2,12 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jetbrains.kotlin</groupId>
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||||
<version>${kotlin.version}</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
{% for dep in dependencies %}
|
{% for dep in dependencies %}
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>{{ dep.groupId }}</groupId>
|
<groupId>{{ dep.groupId }}</groupId>
|
||||||
<artifactId>{{ dep.artifactId }}</artifactId>
|
<artifactId>{{ dep.artifactId }}</artifactId>
|
||||||
<version>{{ "${" }}{{ dep.versionKey }}{{ ".version}" }}</version>
|
<version>{{ "${" }}{{ dep.version.name }}{{ ".version}" }}</version>
|
||||||
{% if dep.scope == "Test" %}
|
{% if dep.scope == "Test" %}
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-bom</artifactId>
|
||||||
|
<version>${kotlin.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
@@ -10,5 +10,7 @@
|
|||||||
|
|
||||||
{% include "starter/pom/plugins/@shade" %}
|
{% include "starter/pom/plugins/@shade" %}
|
||||||
|
|
||||||
|
{% include "starter/pom/plugins/@ktlint" %}
|
||||||
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|||||||
@@ -8,10 +8,11 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<java.version>{{ javaVersion }}</java.version>
|
<java.version>{{ javaVersion }}</java.version>
|
||||||
<kotlin.version>{{ kotlinVersion }}</kotlin.version>
|
<kotlin.version>{{ kotlinVersion }}</kotlin.version>
|
||||||
|
<kotlin.code.style>official</kotlin.code.style>
|
||||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<main.class>{{ basePackage }}/{{ name | lower | capitalize }}Kt</main.class>
|
<main.class>{{ basePackage }}.{{ name | lower | capitalize }}Kt</main.class>
|
||||||
<!-- versions -->
|
<!-- versions -->
|
||||||
{% for version in versions %}
|
{% for version in versions %}
|
||||||
<{{version.name}}.version>{{version.value}}</{{version.name}}.version>
|
<{{version.name}}.version>{{version.value}}</{{version.name}}.version>
|
||||||
@@ -23,6 +24,8 @@
|
|||||||
|
|
||||||
{% include "starter/pom/@repositories" %}
|
{% include "starter/pom/@repositories" %}
|
||||||
|
|
||||||
|
{% include "starter/pom/@dependencyManagement" %}
|
||||||
|
|
||||||
{% include "starter/pom/@plugins" %}
|
{% include "starter/pom/@plugins" %}
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
{% if features | hasFeature("ktlint") %}
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-antrun-plugin</artifactId>
|
||||||
|
<version>1.7</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>ktlint</id>
|
||||||
|
<phase>verify</phase>
|
||||||
|
<configuration>
|
||||||
|
<target name="ktlint">
|
||||||
|
<java taskname="ktlint" dir="${basedir}" fork="true" failonerror="true"
|
||||||
|
classname="com.pinterest.ktlint.Main" classpathref="maven.plugin.classpath">
|
||||||
|
<arg value="src/**/*.kt"/>
|
||||||
|
</java>
|
||||||
|
</target>
|
||||||
|
</configuration>
|
||||||
|
<goals>
|
||||||
|
<goal>run</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>ktlint-format</id>
|
||||||
|
<configuration>
|
||||||
|
<target name="ktlint">
|
||||||
|
<java taskname="ktlint" dir="${basedir}" fork="true" failonerror="true"
|
||||||
|
classname="com.pinterest.ktlint.Main" classpathref="maven.plugin.classpath">
|
||||||
|
<arg value="-F"/>
|
||||||
|
<arg value="src/**/*.kt"/>
|
||||||
|
</java>
|
||||||
|
</target>
|
||||||
|
</configuration>
|
||||||
|
<goals>
|
||||||
|
<goal>run</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.pinterest</groupId>
|
||||||
|
<artifactId>ktlint</artifactId>
|
||||||
|
<version>0.39.0</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</plugin>
|
||||||
|
{% endif %}
|
||||||
@@ -10,6 +10,19 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
|
<section>
|
||||||
|
<h2 class="category">Features</h2>
|
||||||
|
<ul>
|
||||||
|
{% for feature in features %}
|
||||||
|
<label class="m-2">
|
||||||
|
<input name="{{ feature.name }}"
|
||||||
|
type="checkbox"{% if feature.value %} checked{% endif %}>
|
||||||
|
<span>{{ feature.name }}</span>
|
||||||
|
</label>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
{% for category in dependencies %}
|
{% for category in dependencies %}
|
||||||
<section>
|
<section>
|
||||||
<h2 class="category">{{ category.key }}</h2>
|
<h2 class="category">{{ category.key }}</h2>
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package starter
|
||||||
|
|
||||||
|
import starter.config.StarterConfig
|
||||||
|
|
||||||
|
val testConfig = StarterConfig(
|
||||||
|
dependencies = listOf(
|
||||||
|
Dependency(
|
||||||
|
name = "h2",
|
||||||
|
groupId = "com.h2database",
|
||||||
|
artifactId = "h2",
|
||||||
|
version = Version("h2", "1.4.200"),
|
||||||
|
default = false,
|
||||||
|
category = Category.Database,
|
||||||
|
scope = Scope.Compile,
|
||||||
|
logger = null,
|
||||||
|
repository = null,
|
||||||
|
),
|
||||||
|
Dependency(
|
||||||
|
name = "assertj",
|
||||||
|
groupId = "org.assertj",
|
||||||
|
artifactId = "assertj-core",
|
||||||
|
version = Version("assertj", "3.17.2"),
|
||||||
|
default = true,
|
||||||
|
category = Category.Test,
|
||||||
|
scope = Scope.Test,
|
||||||
|
logger = null,
|
||||||
|
repository = null,
|
||||||
|
),
|
||||||
|
Dependency(
|
||||||
|
name = "koin",
|
||||||
|
groupId = "org.koin",
|
||||||
|
artifactId = "koin-core",
|
||||||
|
version = Version("koin", "2.1.6"),
|
||||||
|
default = true,
|
||||||
|
category = Category.Injection,
|
||||||
|
scope = Scope.Compile,
|
||||||
|
logger = null,
|
||||||
|
repository = Repository("jcenter", "https://jcenter.bintray.com"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
inputs = listOf(
|
||||||
|
Input(name = "name", display = "name", value = "example"),
|
||||||
|
Input(name = "basePackage", display = "Base package", value = "org.example"),
|
||||||
|
),
|
||||||
|
features = listOf(Feature("ktlint", true))
|
||||||
|
)
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
package starter.config
|
||||||
|
|
||||||
|
import com.electronwill.nightconfig.toml.TomlParser
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.intellij.lang.annotations.Language
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import starter.*
|
||||||
|
import java.io.StringReader
|
||||||
|
|
||||||
|
internal class ConfigTest {
|
||||||
|
private val parser = TomlParser()
|
||||||
|
private fun parse(config: String) = parser.parse(StringReader(config))
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun inputs() {
|
||||||
|
@Language("toml")
|
||||||
|
val toml = """
|
||||||
|
[inputs]
|
||||||
|
|
||||||
|
[inputs.name]
|
||||||
|
default = "example"
|
||||||
|
display = "Project Name"
|
||||||
|
|
||||||
|
[inputs.basePackage]
|
||||||
|
default = "org.example"
|
||||||
|
display = "Base package"
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
val starterConfig = Config(parse(toml)).load()
|
||||||
|
|
||||||
|
val expected = listOf(
|
||||||
|
Input("basePackage", "Base package", "org.example"),
|
||||||
|
Input("name", "Project Name", "example"),
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(starterConfig.inputs).containsExactlyInAnyOrderElementsOf(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dependencies() {
|
||||||
|
@Language("toml")
|
||||||
|
val toml = """
|
||||||
|
[versions]
|
||||||
|
assertj = "3.17.2"
|
||||||
|
koin = "2.1.6"
|
||||||
|
h2 = "1.4.200"
|
||||||
|
|
||||||
|
[repositories]
|
||||||
|
|
||||||
|
[repositories.jcenter]
|
||||||
|
url = "https://jcenter.bintray.com"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
[dependencies.assertj]
|
||||||
|
groupId = "org.assertj"
|
||||||
|
artifactId = "assertj-core"
|
||||||
|
scope = "test"
|
||||||
|
category = "test"
|
||||||
|
default = true
|
||||||
|
|
||||||
|
[dependencies.koin]
|
||||||
|
groupId = "org.koin"
|
||||||
|
artifactId = "koin-core"
|
||||||
|
category = "injection"
|
||||||
|
repository = "jcenter"
|
||||||
|
default = true
|
||||||
|
|
||||||
|
[dependencies.h2]
|
||||||
|
groupId = "com.h2database"
|
||||||
|
category = "database"
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
val starterConfig = Config(parse(toml)).load()
|
||||||
|
|
||||||
|
val expected = listOf(
|
||||||
|
Dependency(
|
||||||
|
name = "h2",
|
||||||
|
groupId = "com.h2database",
|
||||||
|
artifactId = "h2",
|
||||||
|
version = Version("h2", "1.4.200"),
|
||||||
|
default = false,
|
||||||
|
category = Category.Database,
|
||||||
|
scope = Scope.Compile,
|
||||||
|
logger = null,
|
||||||
|
repository = null,
|
||||||
|
),
|
||||||
|
Dependency(
|
||||||
|
name = "assertj",
|
||||||
|
groupId = "org.assertj",
|
||||||
|
artifactId = "assertj-core",
|
||||||
|
version = Version("assertj", "3.17.2"),
|
||||||
|
default = true,
|
||||||
|
category = Category.Test,
|
||||||
|
scope = Scope.Test,
|
||||||
|
logger = null,
|
||||||
|
repository = null,
|
||||||
|
),
|
||||||
|
Dependency(
|
||||||
|
name = "koin",
|
||||||
|
groupId = "org.koin",
|
||||||
|
artifactId = "koin-core",
|
||||||
|
version = Version("koin", "2.1.6"),
|
||||||
|
default = true,
|
||||||
|
category = Category.Injection,
|
||||||
|
scope = Scope.Compile,
|
||||||
|
logger = null,
|
||||||
|
repository = Repository("jcenter", "https://jcenter.bintray.com"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(starterConfig.dependencies).containsExactlyInAnyOrderElementsOf(expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
package starter.templates
|
||||||
|
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.intellij.lang.annotations.Language
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
import org.koin.dsl.koinApplication
|
||||||
|
import org.w3c.dom.Node
|
||||||
|
import org.w3c.dom.NodeList
|
||||||
|
import starter.Project
|
||||||
|
import starter.config.StarterConfig
|
||||||
|
import starter.modules.configModule
|
||||||
|
import starter.modules.pebbleModule
|
||||||
|
import starter.modules.templateModule
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory
|
||||||
|
import javax.xml.xpath.XPathConstants
|
||||||
|
import javax.xml.xpath.XPathFactory
|
||||||
|
|
||||||
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
|
internal class PomTemplateTest {
|
||||||
|
|
||||||
|
private val koin = koinApplication {
|
||||||
|
modules(configModule, pebbleModule, templateModule)
|
||||||
|
}.koin
|
||||||
|
|
||||||
|
private val pomTemplate = koin.get<PomTemplate>()
|
||||||
|
private val conf = koin.get<StarterConfig>()
|
||||||
|
|
||||||
|
private val project = Project(
|
||||||
|
name = "Test",
|
||||||
|
basePackage = "org.test",
|
||||||
|
inputs = conf.inputs,
|
||||||
|
features = emptyList(),
|
||||||
|
dependencies = conf.dependencies,
|
||||||
|
repositories = conf.dependencies.mapNotNull { it.repository }.toSet()
|
||||||
|
)
|
||||||
|
|
||||||
|
private val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||||
|
private val xPath = XPathFactory.newInstance().newXPath()
|
||||||
|
|
||||||
|
// region xml utils
|
||||||
|
private fun String.extract(expression: String): String {
|
||||||
|
val doc = docBuilder.parse(this.byteInputStream())
|
||||||
|
xPath.compile(expression).evaluate(doc, XPathConstants.NODESET)
|
||||||
|
return xPath.compile(expression).evaluate(doc)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> String.extractAll(expression: String, mapper: (Node) -> T): List<T> {
|
||||||
|
val doc = docBuilder.parse(this.byteInputStream())
|
||||||
|
val res = xPath.compile(expression).evaluate(doc, XPathConstants.NODESET) as NodeList
|
||||||
|
return res.asList().map(mapper)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun NodeList.asList(): List<Node> = (0 until length).map { item(it) }
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun javaVersion() {
|
||||||
|
val xml = pomTemplate.render(project)
|
||||||
|
assertThat(xml.extract("/project/properties/java.version"))
|
||||||
|
.isEqualTo(project.inputs.find { it.name == "javaVersion" }?.value)
|
||||||
|
|
||||||
|
println(xml)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dependencies() {
|
||||||
|
val xml = pomTemplate.render(project)
|
||||||
|
|
||||||
|
val deps = xml.extractAll("/project/dependencies/dependency") {
|
||||||
|
val map = it.childNodes.asList()
|
||||||
|
.filter { it.nodeType == Node.ELEMENT_NODE }
|
||||||
|
.associate { it.nodeName to it.firstChild.nodeValue }
|
||||||
|
|
||||||
|
Triple(map["groupId"]!!, map["artifactId"]!!, map["version"] ?: "")
|
||||||
|
}.filterNot { it.second == "kotlin-stdlib-jdk8" }
|
||||||
|
|
||||||
|
println(deps.joinToString("\n"))
|
||||||
|
|
||||||
|
val expectedDependencies = project.dependencies
|
||||||
|
.map { Triple(it.groupId, it.artifactId, "\${" + it.version.name + ".version}") }
|
||||||
|
|
||||||
|
assertThat(expectedDependencies).containsExactlyInAnyOrderElementsOf(deps)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun versions() {
|
||||||
|
val xml = pomTemplate.render(project)
|
||||||
|
|
||||||
|
val versions = xml.extractAll("/project/properties") {
|
||||||
|
it.childNodes.asList()
|
||||||
|
.filter { it.nodeType == Node.ELEMENT_NODE }
|
||||||
|
.filter { it.nodeName.endsWith(".version") }
|
||||||
|
.filterNot { it.nodeName in listOf("java.version", "kotlin.version") }
|
||||||
|
.associate { it.nodeName.substringBefore(".version") to it.firstChild.nodeValue }
|
||||||
|
}.first()
|
||||||
|
|
||||||
|
val expected = project.dependencies.associate { it.version.name to it.version.value }
|
||||||
|
|
||||||
|
assertThat(versions).containsExactlyInAnyOrderEntriesOf(expected)
|
||||||
|
|
||||||
|
println(versions)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun kotlinxSerialization() {
|
||||||
|
val xml = pomTemplate.render(project)
|
||||||
|
|
||||||
|
@Language("XPath")
|
||||||
|
val kotlinMavenPlugin = "/project/build/plugins/plugin[artifactId='kotlin-maven-plugin']"
|
||||||
|
|
||||||
|
val kotlinxPlugin = "$kotlinMavenPlugin/configuration/compilerPlugins/plugin"
|
||||||
|
assertThat(xml.extract(kotlinxPlugin))
|
||||||
|
.isEqualTo("kotlinx-serialization")
|
||||||
|
|
||||||
|
val kotlinxPluginDep = "$kotlinMavenPlugin/dependencies/dependency/artifactId"
|
||||||
|
assertThat(xml.extract(kotlinxPluginDep))
|
||||||
|
.isEqualTo("kotlin-maven-serialization")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
package starter.utils
|
||||||
|
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.http4k.core.Method
|
||||||
|
import org.http4k.core.Request
|
||||||
|
import org.http4k.core.body.Form
|
||||||
|
import org.http4k.core.body.toBody
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource
|
||||||
|
import org.koin.dsl.koinApplication
|
||||||
|
import org.koin.dsl.module
|
||||||
|
import starter.Feature
|
||||||
|
import starter.Input
|
||||||
|
import starter.Project
|
||||||
|
import starter.modules.routesModule
|
||||||
|
import starter.testConfig
|
||||||
|
import java.util.stream.Stream
|
||||||
|
|
||||||
|
internal class ProjectExtractorImplTest {
|
||||||
|
|
||||||
|
private val fakeModule = module {
|
||||||
|
single { testConfig }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val koin = koinApplication {
|
||||||
|
modules(routesModule, fakeModule)
|
||||||
|
}.koin
|
||||||
|
|
||||||
|
private val projectExtractor = koin.get<ProjectExtractor>()
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
fun invalidProjectsSource(): Stream<Form> = Stream.of(
|
||||||
|
emptyList(),
|
||||||
|
listOf(
|
||||||
|
"basePackage" to "org.example",
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
"basePackage" to "org.example/a",
|
||||||
|
"name" to "test"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("invalidProjectsSource")
|
||||||
|
fun invalidProjects(form: Form) {
|
||||||
|
val request = Request(Method.POST, "")
|
||||||
|
.body(form.toBody())
|
||||||
|
|
||||||
|
val project = projectExtractor(request)
|
||||||
|
|
||||||
|
assertThat(project).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
fun validProjectsSource(): Stream<Pair<Form, Project>> = Stream.of(
|
||||||
|
listOf(
|
||||||
|
"name" to "Test",
|
||||||
|
"basePackage" to "org.example",
|
||||||
|
"ktlint" to "",
|
||||||
|
) to Project(
|
||||||
|
name = "Test",
|
||||||
|
basePackage = "org.example",
|
||||||
|
inputs = listOf(
|
||||||
|
Input(name = "name", display = "name", value = "Test"),
|
||||||
|
Input(name = "basePackage", display = "Base package", value = "org.example"),
|
||||||
|
),
|
||||||
|
features = listOf(
|
||||||
|
Feature(name = "ktlint", value = true)
|
||||||
|
),
|
||||||
|
dependencies = listOf(),
|
||||||
|
repositories = setOf(),
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
"name" to "Test",
|
||||||
|
"basePackage" to "org.example",
|
||||||
|
"ktlint" to "",
|
||||||
|
"koin" to "",
|
||||||
|
) to Project(
|
||||||
|
name = "Test",
|
||||||
|
basePackage = "org.example",
|
||||||
|
inputs = listOf(
|
||||||
|
Input(name = "name", display = "name", value = "Test"),
|
||||||
|
Input(name = "basePackage", display = "Base package", value = "org.example"),
|
||||||
|
),
|
||||||
|
features = listOf(
|
||||||
|
Feature(name = "ktlint", value = true)
|
||||||
|
),
|
||||||
|
dependencies = listOf(
|
||||||
|
testConfig.dependencies.find { it.name == "koin" }!!
|
||||||
|
),
|
||||||
|
repositories = setOf(
|
||||||
|
testConfig.dependencies.find { it.name == "koin" }!!.repository!!
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("validProjectsSource")
|
||||||
|
fun validProjects(pair: Pair<Form, Project>) {
|
||||||
|
val (form, expected) = pair
|
||||||
|
|
||||||
|
val request = Request(Method.POST, "")
|
||||||
|
.body(form.toBody())
|
||||||
|
|
||||||
|
val project = projectExtractor(request)
|
||||||
|
|
||||||
|
assertThat(project).isEqualTo(expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
junit.jupiter.testinstance.lifecycle.default=per_class
|
||||||
|
junit.jupiter.execution.parallel.enabled=true
|
||||||
|
junit.jupiter.execution.parallel.mode.default=same_thread
|
||||||
|
junit.jupiter.execution.parallel.mode.classes.default=concurrent
|
||||||
Reference in New Issue
Block a user