Receiving form params to Ktor server - kotlin

I'm new to Java and Kotlin, trying to build a contact form with Ktor, so I enabled the insecure connection of my gmail from here, and built the app below:
blogApp.kt:
package blog
import org.jetbrains.ktor.netty.*
import org.jetbrains.ktor.routing.*
import org.jetbrains.ktor.application.*
import org.jetbrains.ktor.features.*
import org.jetbrains.ktor.host.*
import org.jetbrains.ktor.http.*
import org.jetbrains.ktor.response.*
import org.apache.commons.mail.*
fun Application.module() {
install(DefaultHeaders)
install(CallLogging)
install(Routing) {
get("/") {
call.respondText("""
My Example Blog2
<form action="/contact-us" method="post">
<input name="subject" placeholder="Subject">
<br>
<textarea name="message" placeholder="Your message ..."></textarea>
<br>
<button>Submit</button>
</form>
""", ContentType.Text.Html)
}
post("/contact-us") {
SimpleEmail().apply {
setHostName("smtp.gmail.com")
setSmtpPort(465)
setAuthenticator(DefaultAuthenticator("my_alias#gmail.com", "my_gmil_passoword"))
setSSLOnConnect(true)
setFrom("my_alias#gmail.com")
setSubject("subject") // I need to use formParam
setMsg("message") // I need to use formParam
addTo("my_alias#gmail.com")
}.send() // will throw email-exception if something is wrong
call.respondRedirect("/contact-us/success")
}
get("/contact-us/success") {
call.respondText("Your message was sent", ContentType.Text.Html)
}
}
}
fun main(args: Array<String>) {
embeddedServer(Netty, 8080, watchPaths = listOf("BlogAppKt"), module = Application::module).start()
}
build.gradle:
group 'Example'
version 'alpha'
buildscript {
ext.kotlin_version = '1.1.4-3'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'java'
apply plugin: 'kotlin'
sourceCompatibility = 1.8
ext.ktor_version = '0.4.0'
repositories {
mavenCentral()
maven { url "http://dl.bintray.com/kotlin/ktor" }
maven { url "https://dl.bintray.com/kotlin/kotlinx" }
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
compile "org.jetbrains.ktor:ktor-core:$ktor_version"
compile "org.jetbrains.ktor:ktor-netty:$ktor_version"
compile "org.apache.commons:commons-email:1.4"
compile "org.slf4j:slf4j-simple:1.7.25"
compile "ch.qos.logback:logback-classic:1.2.1"
testCompile group: 'junit', name: 'junit', version: '4.12'
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
kotlin {
experimental {
coroutines "enable"
}
}
jar {
baseName 'abc'
manifest {
attributes 'Main-Class': 'blog.BlogAppKt'
}
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
and things gone smoothly with it, I was able to send an email to myself then redirect to the success page, but the message got sent is with pre-set data:
setSubject("subject") // I need to use formParam
setMsg("message") // I need to use formParam
how can I make the Ktor receive the data the user really entered in the form, how can I read the form params?

You can use call.receive<ValuesMap>() and introspect the data:
import org.jetbrains.ktor.request.* // for recieve
import org.jetbrains.ktor.util.* // for ValuesMap
post("/contact-us") {
val post = call.receive<ValuesMap>()
val subj = post["subject"]
val msg = post["message"]
SimpleEmail().apply { ... }
}
NOTE: ValuesMap is deprecated in latest ktor version, so use the following code:
val post = call.receiveParameters()

Well although the above answer would have been correct at that point of time, today when I am going through kotlin + Ktor, I see that the above answer is not valid anymore.
What you need now is something like this:
call.receive< Parameters >()["PARAM_NAME"]
Parameters class is in the following package: io.ktor.http.Parameters
Nikhil

Related

Building Kotlin TornadoFx into exe for Windows

I have an application written in Kotlin and I used TornadoFx to build a GUI around it. As stated in the TornadoFx guide, I used OpenJDK11 and set kotlin.jvmtarget to "11". Here is my gradle build:
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.4.30'
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.9'
id 'java'
id 'edu.sc.seis.launch4j' version '2.4.9'
}
group = 'me.nicola'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
application {
mainClassName = "Engine"
}
javafx {
version = "11.0.2"
modules = ['javafx.controls', 'javafx.graphics']
}
dependencies {
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit'
implementation 'no.tornado:tornadofx:1.7.20'
implementation group: 'org.apache.commons', name: 'commons-email', version: '1.5'
implementation 'com.github.doyaaaaaken:kotlin-csv-jvm:0.15.0'
}
test {
useJUnit()
}
jar {
manifest {
attributes "Main-Class" : "EngineKt"
}
}
compileKotlin {
kotlinOptions.jvmTarget = "11"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "11"
}
task fatJar(type: Jar) {
baseName 'PrimaNotaHelper'
manifest {
attributes "Main-Class": "EngineKt"
}
from {
configurations.runtimeClasspath.collect {
it.isDirectory() ? it : zipTree(it)
}
}
with jar
}
launch4j {
mainClassName = 'EngineKt'
//icon = "${projectDir}/icons/myApp.ico"
}
I'm trying to use Launch4J to bundle my app into an executable for windows (.exe) and it compiles correctly when I launch the "launch4j" task, building the "lib" folder and the exe file. However, once I'm on Windows the executable does not work.
I also tried to use the Launch4J GUI for Windows and played around a little bit with that but nothing works for me: either It simply crash before starting or ask me for a different version of the JRE.
Can someone help me to set up my build correctly?
Thank you so much!

How to add coverage report (JaCoCo) to kotest based using build.gradle.kts?

I use Kotlin for server side, and I want to add test coverage (by using JaCoCo but can be any other open source).
At the moment, I don't find any documentation to explain how to enable/add coverage report to kotlin project based on kotest which actually works.
(Yes I tried the official documentation)
build.gradle.kts:
import Libraries.coroutines
import Libraries.koTest
import Libraries.koTestCore
import Libraries.mockk
import Libraries.testcontainers
import Libraries.testcontainersPostgres
plugins {
java
`kotlin-dsl` version "1.3.5" apply false
kotlin("jvm") version Versions.kotlin_version
kotlin("plugin.serialization") version Versions.kotlin_version apply false
id("com.diffplug.gradle.spotless") version Plugins.spotless
id("com.palantir.docker") version Plugins.docker apply false
id("com.palantir.docker-run") version Plugins.docker apply false
id("com.google.protobuf") version Plugins.protobuf apply false
id("org.flywaydb.flyway") version Plugins.flyway apply false
}
allprojects {
group = "my-app"
version = "2.0"
apply(plugin = "kotlin")
apply(plugin = "com.diffplug.gradle.spotless")
repositories {
mavenCentral()
jcenter()
}
tasks {
compileKotlin {
dependsOn(spotlessApply)
kotlinOptions {
jvmTarget = "11"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "11"
}
}
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
tasks.withType<Test> {
useJUnitPlatform()
maxParallelForks = 1
environment("ENV", "test")
}
afterEvaluate {
project.configurations.forEach {
// Workaround the Gradle bug resolving multiplatform dependencies.
if (it.name.contains("Proto")) {
it.attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, Usage.JAVA_RUNTIME))
}
}
}
}
subprojects {
spotless {
kotlin {
ktlint().userData(mapOf("disabled_rules" to "no-wildcard-imports"))
trimTrailingWhitespace()
endWithNewline()
}
}
dependencies {
implementation(coroutines)
testImplementation(koTest)
testImplementation(koTestCore)
testImplementation(mockk)
testImplementation(testcontainers)
testImplementation(testcontainersPostgres)
}
configurations {
all {
resolutionStrategy.setForcedModules("org.apache.httpcomponents:httpclient:4.5.9")
}
}
}
Any help will be great. Thanks in advance.
in my experience, Jacoco and Kotest interoperate just fine out of the box. I'm using it and all I need to do is the following:
plugins {
jacoco
// The rest of your plugins
}
// Your build, as it was before
You can inspect a working Jacoco + Kotest build here.
To verify that it works, you can e.g., feed the Jacoco generated data to services such as Codecov.io.
First, generate the report in XML format:
tasks.jacocoTestReport {
reports {
xml.isEnabled = true
}
}
And then use the Codecov.io service to fetch and analyze the results:
bash <(curl -s https://codecov.io/bash)
As you can see in this report, Jacoco is doing its job without further configuration.

Kotlin Gradle Cucumber: Undefined Step

I am building a selenium automation project using cucumber and kotlin, but after I have set up the skeleton. The steps can't be identified by cucumber. I mainly call gradle cucumber on terminal to begin the test.
build.gradle
plugins {
id 'java'
id 'org.jetbrains.kotlin.jvm' version '1.3.21'
id "com.github.spacialcircumstances.gradle-cucumber-reporting" version "0.1.7"
}
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
jcenter()
mavenCentral()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
testCompile group: 'junit', name: 'junit', version: '4.12'
testImplementation 'io.cucumber:cucumber-java8:4.7.1'
testImplementation 'io.cucumber:cucumber-junit:4.3.1'
implementation group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '3.141.59'
testImplementation 'no.tornado:tornadofx:1.7.17'
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
configurations {
cucumberRuntime {
extendsFrom testImplementation
}
}
task cucumber() {
dependsOn assemble, compileTestJava
doLast {
javaexec {
main = "cucumber.api.cli.Main"
classpath = configurations.cucumberRuntime + sourceSets.test.output
args = ['--plugin', 'json:test_reports/' + getDate() + '_automation_test_result.json', '--glue', 'src/test/kotlin/steps', 'src/test/resources/feature']
}
}
}
cucumberReports {
outputDir = file('test_reports/' + getDate()+ '_html')
buildId = '0'
reports = files('test_reports/' + getDate() + '_automation_test_result.json', 'test_reports/cucumber-json.json')
}
static def getDate() {
def date = new Date()
def formattedDate = date.format('yyyy-MM-dd')
return formattedDate
}
Below is the structure:
This is the folders
Below is the result:
Task :cucumber
Sep 18, 2019 5:27:40 PM cucumber.api.cli.Main run
WARNING: You are using deprecated Main class. Please use io.cucumber.core.cli.Main
Undefined scenarios:
src/test/resources/feature/test.feature:15 # Input something in the search bar
src/test/resources/feature/test.feature:16 # Input something in the search bar
src/test/resources/feature/test.feature:25 # Change to different sub-page
src/test/resources/feature/test.feature:26 # Change to different sub-page
src/test/resources/feature/test.feature:27 # Change to different sub-page
src/test/resources/feature/test.feature:28 # Change to different sub-page
6 Scenarios (6 undefined)
30 Steps (30 undefined)
0m0.100s
You can implement missing steps with the snippets below:
Given("I have logged in as a super admin", () -> {
// Write code here that turns the phrase above into concrete actions
throw new cucumber.api.PendingException();
});
Given("I have navigated to CRM", () -> {
// Write code here that turns the phrase above into concrete actions
throw new cucumber.api.PendingException();
});
Given("I have clicked the client button on the navigation bar", () -> {
// Write code here that turns the phrase above into concrete actions
throw new cucumber.api.PendingException();
});
When("I input {string} in the search bar", (String string) -> {
// Write code here that turns the phrase above into concrete actions
throw new cucumber.api.PendingException();
});
Then("the search result should contain {string}", (String string) -> {
// Write code here that turns the phrase above into concrete actions
throw new cucumber.api.PendingException();
});
Given("I have navigated to the home page on CRM", () -> {
// Write code here that turns the phrase above into concrete actions
throw new cucumber.api.PendingException();
});
When("I click the {string} on the navigation bar", (String string) -> {
// Write code here that turns the phrase above into concrete actions
throw new cucumber.api.PendingException();
});
Then("I should be brought to different page", () -> {
// Write code here that turns the phrase above into concrete actions
throw new cucumber.api.PendingException();
});
BUILD SUCCESSFUL in 2s
4 actionable tasks: 1 executed, 3 up-to-date
I can navigate to the step file when I ctrl+click on the implemented Given or When steps. It seems it can't find the steps definition when it runs. What am i doing wrong here?
I had this exact issue and the answer above is correct, but a bit unclear. (I removed everything else rather than leaving the resources folder in there too!)
This is the task cucumber() section of my build.gradle file:
task cucumber() {
dependsOn assemble, compileTestKotlin
doLast {
javaexec {
main = "io.cucumber.core.cli.Main"
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
args = ['--plugin', 'pretty', '--glue', 'hellocucumber', 'src/test/resources']
}
}
}
where hellocucumber is my package name, as outlined in this example.
I hope it helps!

Issue using Mobbeel Fataar plugin with uploadArchives task

I'm in the process of trying to create a fat AAR to distribute my Android library. I'm using the Mobbeel Fataar plugin to package all of my dependencies into an AAR for ease of distribution. This works perfectly, and when I run ./gradlew build, I get an AAR file in mylibrary/build/outputs/aar/ called mylibrary-release.aar and it includes all of my dependencies in its libs directory. The issue arises when I attempt to distribute the AAR.
In order to distribute, I found this tutorial to take me through it, which I'm using virtually unchanged. Here's that code (in my build.gradle):
apply plugin: 'maven'
apply plugin: 'signing'
def isReleaseBuild() {
return VERSION_NAME.contains("SNAPSHOT") == false
}
def getReleaseRepositoryUrl() {
return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL
: "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
}
def getSnapshotRepositoryUrl() {
return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL
: "https://oss.sonatype.org/content/repositories/snapshots/"
}
def getRepositoryUsername() {
return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : ""
}
def getRepositoryPassword() {
return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : ""
}
afterEvaluate { project ->
uploadArchives {
repositories {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
pom.groupId = GROUP
pom.artifactId = POM_ARTIFACT_ID
pom.version = VERSION_NAME
repository(url: getReleaseRepositoryUrl()) {
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
}
snapshotRepository(url: getSnapshotRepositoryUrl()) {
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
}
pom.project {
name POM_NAME
packaging POM_PACKAGING
description POM_DESCRIPTION
url POM_URL
scm {
url POM_SCM_URL
connection POM_SCM_CONNECTION
developerConnection POM_SCM_DEV_CONNECTION
}
licenses {
license {
name POM_LICENCE_NAME
url POM_LICENCE_URL
distribution POM_LICENCE_DIST
}
}
developers {
developer {
id POM_DEVELOPER_ID
name POM_DEVELOPER_NAME
}
}
}
}
}
}
signing {
required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
sign configurations.archives
}
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.sourceFiles
}
artifacts {
archives androidSourcesJar
}
}
The issue is that, when I run ./gradlew :mylibrary:uploadArchives, it creates a mylibrary-1.0.aar file in mylibrary/build/outputs/aar/ and uploads that file. The problem is that it doesn't have my dependencies bundled in.
I'd like to somehow create mylibrary-1.0.aar with all of my dependencies bundled, and then have that be uploaded. Unfortunately I don't have a completely clear understanding of how the upload code I'm using works. Does anyone know how I can do what I want?

How can I add data to a Kotlin object and get it on a Vuejs page

I'm absolutely new to Kotlin. I'm trying to make a simple object on the backend side by Kotlin and get it on frontend Vuejs. How can I do something like this (this is the raw code of HeaderBar.kt, all my attempts were denied by compiler):
object HeaderBar {
val computed = object {
fun items(): Array<Item> {
items.add(Item(
"NY",
"Bill"
))
return items
}
}
data class Item(
val city: String,
val name: String
)
}
on Kotlin side?
And get the items on HeaderBar.vue. I'm not sure, but I do this by:
<template>
<div class="main-header">
<div v-for="item in items" class="items">
<span class="city">{{item.city}}</span>
<span class="name">{{item.name}}</span>
</div>
</div>
<template>
<script>
export default path.to.HeaderBar
</script>
First off all it's not simple question. Kotlin/Js not so mature as Kotlin/Jvm so there are many not so simple tasks.
First you need to somehow compile to javascript and then you need to attach Vue to kotlin/javascript code.
Webpack can make it easer, so I write a simple example to show you how to write your example in Kotlin.
!Warning!: all code below is just draft (and has been writen only in demonstration purpose), so use it in your projects with special caution!
Lets create project with below structure:
Application.kt:
package vue_test
fun VueJs(init: VueContext.() -> Unit) = Vue(VueContext().apply(init))
class VueContext {
var el: String = ""
var data: dynamic = js("{}")
}
fun main(args: Array<String>) {
val app: dynamic = VueJs {
el = "#app"
data = mapOf("items" to listOf(
Item("NY", "Bill"),
Item("Test", "Test2")
)).toJs()
}
}
data class Item(
val city: String,
val name: String
)
fun Map<String, Any>.toJs(): dynamic {
val result: dynamic = object {}
for ((key, value) in this) {
when (value) {
is String -> result[key] = value
is List<*> -> result[key] = (value as List<Any>).toJs()
else -> throw RuntimeException("value has invalid type")
}
}
return result
}
fun List<Any>.toJs(): dynamic {
val result: dynamic = js("[]")
for (value in this) {
when (value) {
is String -> result.push(value)
is Item -> {
result.push(value.toJs())
}
else -> throw RuntimeException("value has invalid type")
}
}
return result
}
fun Item.toJs(): dynamic {
val result: dynamic = object {}
result["city"] = this.city
result["name"] = this.name
return result
}
I have write few function toJs which converts Kotlin object to Js object. It theory you may use JSON serialization to simplify this, or other more simple solution (if exists).
Vue.kt
#file:JsModule("vue")
package vue_test
#JsName("default")
external open class Vue(init: dynamic)
In this file we have only Vue declarations.
index.html
<!DOCTYPE html>
<html>
<head>
<title>Test project</title>
</head>
<body class="testApp">
<h1>Kotlin-Js test</h1>
<div id="app">
<div class="main-header">
<div v-for="item in items" class="items">
<span class="city">{{item.city}}</span>
<span class="name">{{item.name}}</span>
</div>
</div>
</div>
<script type="text/javascript" language="JavaScript" src="frontend.bundle.js"></script>
</body>
</html>
Buldle has been created by webpack, and I has put this script to bottom because Vue needed to start his manipulations only then all necessary html tags has been already exists.
My build.gradle file with kotlin-frontend plugin and kotlin-js plugin:
buildscript {
ext.kotlin_version = '1.2.10'
repositories {
mavenCentral()
jcenter()
maven {
url "https://dl.bintray.com/kotlin/kotlin-eap"
}
maven {
url "https://repo.gradle.org/gradle/libs-releases-local"
}
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-frontend-plugin:0.0.21"
}
}
group 'test'
version '1.0-SNAPSHOT'
apply plugin: 'kotlin-platform-js'
apply plugin: 'org.jetbrains.kotlin.frontend'
repositories {
mavenCentral()
}
kotlinFrontend {
sourceMaps = true
npm {
dependency("vue")
}
webpackBundle {
port = 8080
bundleName = "frontend"
contentPath = file('src/main/web')
webpackConfigFile = project.projectDir.path + '/webpack.config.js'
}
}
compileKotlin2Js {
kotlinOptions.metaInfo = true
kotlinOptions.outputFile = "$project.buildDir.path/js/${project.name}.js"
kotlinOptions.sourceMap = true
kotlinOptions.moduleKind = 'commonjs'
kotlinOptions.main = "call"
}
kotlin {
experimental {
coroutines 'enable'
}
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version"
}
settings.gradle
rootProject.name = 'test-kotlin-vue'
and last file, custom webpack configuration:
var config = require('./build/WebPackHelper.js')
var path = require('path')
module.exports = {
entry: config.moduleName,
output: {
path: path.resolve('./bundle'),
publicPath: '/build/',
filename: 'frontend.bundle.js'
},
module: {
rules: []
},
resolve: {
modules: [path.resolve('js'), path.resolve('..', 'src'), path.resolve('.'), path.resolve('node_modules')],
extensions: ['.js', '.css'],
alias: {
'vue$': 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' for webpack 1
}
},
devtool: '#source-map'
};
console.log(module.exports.resolve.modules);
With kotlin-frontend plugin you could not use separate webpack config, but in this example Vue needed a full version to compile template, so it's needed to add alias in webpack. And I don't know how to do this in build.gradle.
Hope this will help you!
To start project with dev bundle run this command: gradle build webpack-run, and then open http://localhost:8080 in your browser
To stop test run command: gradle webpack-stop