diff --git a/pom.xml b/pom.xml
index 779057a..69f3ab8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -64,6 +64,12 @@
5.7.0
test
+
+ org.junit.jupiter
+ junit-jupiter-params
+ 5.7.0
+ test
+
org.assertj
assertj-core
diff --git a/src/main/kotlin/starter/Models.kt b/src/main/kotlin/starter/Models.kt
index c02efeb..65255f6 100644
--- a/src/main/kotlin/starter/Models.kt
+++ b/src/main/kotlin/starter/Models.kt
@@ -32,7 +32,7 @@ data class Project(
val inputs: List,
val features: List,
val dependencies: List,
- val repositories: Collection,
+ val repositories: Set,
)
data class Version(val name: String, val value: String)
diff --git a/src/main/kotlin/starter/modules/PebbleModule.kt b/src/main/kotlin/starter/modules/PebbleModule.kt
index 98b8c2f..7e490b9 100644
--- a/src/main/kotlin/starter/modules/PebbleModule.kt
+++ b/src/main/kotlin/starter/modules/PebbleModule.kt
@@ -2,8 +2,8 @@ package starter.modules
import org.koin.dsl.bind
import org.koin.dsl.module
-import pebble.DepAsXmlPebbleFunction
-import pebble.HasFeatureFilter
+import starter.pebble.DepAsXmlPebbleFunction
+import starter.pebble.HasFeatureFilter
import starter.utils.PebbleEngineBuilder
import starter.utils.PebbleEngineBuilder.CacheType.ConcurrentMap
import starter.utils.PebbleFilter
diff --git a/src/main/kotlin/starter/modules/RoutesModule.kt b/src/main/kotlin/starter/modules/RoutesModule.kt
index e6ebd1d..647ab8b 100644
--- a/src/main/kotlin/starter/modules/RoutesModule.kt
+++ b/src/main/kotlin/starter/modules/RoutesModule.kt
@@ -11,10 +11,13 @@ import starter.routes.IndexRouteSupplier
import starter.routes.RouteSupplier
import starter.routes.ZipRouteSupplier
import starter.routes.toRouter
+import starter.utils.ProjectExtractor
+import starter.utils.ProjectExtractorImpl
val routesModule = module {
single { IndexRouteSupplier(get()) } bind RouteSupplier::class
single { ZipRouteSupplier(get(), get()) } bind RouteSupplier::class
+ single { ProjectExtractorImpl(get()) }
single {
ServerFilters.CatchAll().then(
routes(
diff --git a/src/main/kotlin/pebble/DepAsXmlPebbleFunction.kt b/src/main/kotlin/starter/pebble/DepAsXmlPebbleFunction.kt
similarity index 98%
rename from src/main/kotlin/pebble/DepAsXmlPebbleFunction.kt
rename to src/main/kotlin/starter/pebble/DepAsXmlPebbleFunction.kt
index d63036f..4d59a5d 100644
--- a/src/main/kotlin/pebble/DepAsXmlPebbleFunction.kt
+++ b/src/main/kotlin/starter/pebble/DepAsXmlPebbleFunction.kt
@@ -1,4 +1,4 @@
-package pebble
+package starter.pebble
import com.mitchellbosecke.pebble.extension.escaper.SafeString
import com.mitchellbosecke.pebble.template.EvaluationContext
diff --git a/src/main/kotlin/pebble/HasFeatureFilter.kt b/src/main/kotlin/starter/pebble/HasFeatureFilter.kt
similarity index 96%
rename from src/main/kotlin/pebble/HasFeatureFilter.kt
rename to src/main/kotlin/starter/pebble/HasFeatureFilter.kt
index 1b5d5d0..f51ad28 100644
--- a/src/main/kotlin/pebble/HasFeatureFilter.kt
+++ b/src/main/kotlin/starter/pebble/HasFeatureFilter.kt
@@ -1,4 +1,4 @@
-package pebble
+package starter.pebble
import com.mitchellbosecke.pebble.template.EvaluationContext
import com.mitchellbosecke.pebble.template.PebbleTemplate
diff --git a/src/main/kotlin/starter/routes/ZipRouteSupplier.kt b/src/main/kotlin/starter/routes/ZipRouteSupplier.kt
index 38ecabf..3798ce7 100644
--- a/src/main/kotlin/starter/routes/ZipRouteSupplier.kt
+++ b/src/main/kotlin/starter/routes/ZipRouteSupplier.kt
@@ -1,62 +1,35 @@
package starter.routes
import org.http4k.core.Method
+import org.http4k.core.Request
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.routing.bind
-import starter.Project
import starter.ProjectZip
-import starter.config.StarterConfig
import starter.extensions.attachment
import starter.extensions.badRequest
import starter.extensions.ok
+import starter.utils.ProjectExtractor
import java.io.ByteArrayInputStream
class ZipRouteSupplier(
- private val conf: StarterConfig,
+ private val projectExtractor: ProjectExtractor,
private val projectZip: ProjectZip,
) : 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 {
- req.form(it.name) != null
- }
+ val outputStream = projectZip.createZip(project)
- val formMap = req.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!!
- val basePackage = inputs.find { it.name == "basePackage" }!!.value!!
-
- if (basePackage.contains("/") || basePackage.contains("..")) {
- Response.badRequest()
- } else {
- val repositories = deps.mapNotNull { it.repository }.toSet()
- val project = Project(projectName, basePackage, inputs, features, deps, repositories)
- val outputStream = projectZip.createZip(project)
-
- Response.ok().with(
- attachment(
- value = ByteArrayInputStream(outputStream.toByteArray()),
- name = "$projectName.zip",
- contentType = "application/zip"
- )
+ return Response.ok().with(
+ attachment(
+ value = ByteArrayInputStream(outputStream.toByteArray()),
+ name = "${project.name}.zip",
+ contentType = "application/zip"
)
- }
+ )
}
+
+ override fun get() = "/" bind Method.POST to ::handle
}
diff --git a/src/main/kotlin/starter/templates/PomTemplate.kt b/src/main/kotlin/starter/templates/PomTemplate.kt
index 774912e..66cda6e 100644
--- a/src/main/kotlin/starter/templates/PomTemplate.kt
+++ b/src/main/kotlin/starter/templates/PomTemplate.kt
@@ -21,8 +21,6 @@ class PomTemplate(private val engine: PebbleEngine) : Template {
args[it.name] = it.value
}
- println(args.entries.joinToString("\n"))
-
val rendered = engine.render("starter/pom/index", args)
return prettyPrintXml(rendered)
}
diff --git a/src/main/kotlin/starter/utils/ProjectExtractor.kt b/src/main/kotlin/starter/utils/ProjectExtractor.kt
new file mode 100644
index 0000000..69450d0
--- /dev/null
+++ b/src/main/kotlin/starter/utils/ProjectExtractor.kt
@@ -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)
+ }
+ }
+}
diff --git a/src/test/kotlin/starter/TestProject.kt b/src/test/kotlin/starter/TestProject.kt
new file mode 100644
index 0000000..47c4d25
--- /dev/null
+++ b/src/test/kotlin/starter/TestProject.kt
@@ -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))
+)
diff --git a/src/test/kotlin/starter/utils/ProjectExtractorImplTest.kt b/src/test/kotlin/starter/utils/ProjectExtractorImplTest.kt
new file mode 100644
index 0000000..ca60f63
--- /dev/null
+++ b/src/test/kotlin/starter/utils/ProjectExtractorImplTest.kt
@@ -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()
+
+ @Suppress("unused")
+ fun invalidProjectsSource(): Stream