diff --git a/material/conferences/2024-02-laug-meetup/kmp-wizard-fullstack/server/bin/main/logback.xml b/material/conferences/2024-02-laug-meetup/kmp-wizard-fullstack/server/bin/main/logback.xml
new file mode 100644
index 00000000..bdbb64ec
--- /dev/null
+++ b/material/conferences/2024-02-laug-meetup/kmp-wizard-fullstack/server/bin/main/logback.xml
@@ -0,0 +1,12 @@
+
+
+
+ %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
diff --git a/material/conferences/2024-02-laug-meetup/kmp-wizard-fullstack/server/bin/main/org/example/project/Application.kt b/material/conferences/2024-02-laug-meetup/kmp-wizard-fullstack/server/bin/main/org/example/project/Application.kt
new file mode 100644
index 00000000..8b85cf60
--- /dev/null
+++ b/material/conferences/2024-02-laug-meetup/kmp-wizard-fullstack/server/bin/main/org/example/project/Application.kt
@@ -0,0 +1,22 @@
+package org.example.project
+
+import Greeting
+import SERVER_PORT
+import io.ktor.server.application.*
+import io.ktor.server.engine.*
+import io.ktor.server.netty.*
+import io.ktor.server.response.*
+import io.ktor.server.routing.*
+
+fun main() {
+ embeddedServer(Netty, port = SERVER_PORT, host = "0.0.0.0", module = Application::module)
+ .start(wait = true)
+}
+
+fun Application.module() {
+ routing {
+ get("/") {
+ call.respondText("Ktor: ${Greeting().greet()}")
+ }
+ }
+}
diff --git a/material/conferences/2024-02-laug-meetup/kmp-wizard-multiplatform-library-template-main/.github/workflows/deploy.yml b/material/conferences/2024-02-laug-meetup/kmp-wizard-multiplatform-library-template-main/.github/workflows/deploy.yml
deleted file mode 100644
index 29eed898..00000000
--- a/material/conferences/2024-02-laug-meetup/kmp-wizard-multiplatform-library-template-main/.github/workflows/deploy.yml
+++ /dev/null
@@ -1,63 +0,0 @@
-# This workflow uses actions that are not certified by GitHub.
-# They are provided by a third-party and are governed by
-# separate terms of service, privacy policy, and support
-# documentation.
-# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
-# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
-
-name: Deploy to central
-
-on: workflow_dispatch
-
-permissions:
- contents: read
-
-jobs:
- build:
- uses: ./.github/workflows/gradle.yml
- deploy:
- needs: build
- strategy:
- matrix:
- include:
- - target: publishIosArm64PublicationToSonatypeRepository
- os: macos-latest
- - target: publishAndroidReleasePublicationToSonatypeRepository
- os: ubuntu-latest
- - target: publishJvmPublicationToSonatypeRepository
- os: ubuntu-latest
- - target: publishLinuxX64PublicationToSonatypeRepository
- os: ubuntu-latest
- - target: publishKotlinMultiplatformPublicationToSonatypeRepository
- os: ubuntu-latest
- runs-on: ${{ matrix.os }}
- steps:
- - uses: actions/checkout@v3
- - name: Validate Gradle Wrapper
- uses: gradle/wrapper-validation-action@v1
- - uses: actions/cache@v3
- with:
- path: |
- ~/.konan
- key: ${{ runner.os }}-${{ hashFiles('**/.lock') }}
- - name: Import GPG key
- uses: crazy-max/ghaction-import-gpg@v5
- with:
- gpg_private_key: ${{ secrets.OSSRH_GPG_SECRET_KEY }}
- passphrase: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }}
- - name: Set up JDK 17
- uses: actions/setup-java@v3
- with:
- java-version: '17'
- distribution: 'temurin'
- - name: Gradle publish
- uses: gradle/gradle-build-action@ce999babab2de1c4b649dc15f0ee67e6246c994f
- with:
- arguments: |
- ${{ matrix.target }}
- closeSonatypeStagingRepository
- -Psigning.gnupg.passphrase='${{secrets.OSSRH_GPG_SECRET_KEY_PASSWORD}}'
- -Psigning.gnupg.keyName='${{secrets.OSSRH_GPG_SECRET_KEY_ID}}'
- -PsonatypeUsername='${{secrets.OSSRH_USERNAME}}'
- -PsonatypePassword='${{secrets.OSSRH_PASSWORD}}'
-
diff --git a/material/conferences/2024-02-laug-meetup/kmp-wizard-multiplatform-library-template-main/.github/workflows/gradle.yml b/material/conferences/2024-02-laug-meetup/kmp-wizard-multiplatform-library-template-main/.github/workflows/gradle.yml
deleted file mode 100644
index 608253f6..00000000
--- a/material/conferences/2024-02-laug-meetup/kmp-wizard-multiplatform-library-template-main/.github/workflows/gradle.yml
+++ /dev/null
@@ -1,54 +0,0 @@
-# This workflow uses actions that are not certified by GitHub.
-# They are provided by a third-party and are governed by
-# separate terms of service, privacy policy, and support
-# documentation.
-# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
-# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
-
-name: Java CI with Gradle
-
-on:
- push:
- branches: [ "main" ]
- pull_request:
- branches: [ "main" ]
- workflow_call:
-
-permissions:
- contents: read
-
-jobs:
- build:
- strategy:
- matrix:
- include:
- - target: iosSimulatorArm64Test
- os: macos-latest
- - target: jvmTest
- os: ubuntu-latest
- - target: linuxX64Test
- os: ubuntu-latest
- - target: testDebugUnitTest
- os: ubuntu-latest
- - target: testReleaseUnitTest
- os: ubuntu-latest
- runs-on: ${{ matrix.os }}
-
- steps:
- - uses: actions/checkout@v3
- - name: Validate Gradle Wrapper
- uses: gradle/wrapper-validation-action@v1
- - uses: actions/cache@v3
- with:
- path: |
- ~/.konan
- key: ${{ runner.os }}-${{ hashFiles('**/.lock') }}
- - name: Set up JDK 17
- uses: actions/setup-java@v3
- with:
- java-version: '17'
- distribution: 'temurin'
- - name: Build with Gradle
- uses: gradle/gradle-build-action@ce999babab2de1c4b649dc15f0ee67e6246c994f
- with:
- arguments: ${{ matrix.target }}
diff --git a/material/conferences/2024-02-laug-meetup/kmp-wizard-multiplatform-library-template-main/.gitignore b/material/conferences/2024-02-laug-meetup/kmp-wizard-multiplatform-library-template-main/.gitignore
index e510fa99..5ee6a246 100644
--- a/material/conferences/2024-02-laug-meetup/kmp-wizard-multiplatform-library-template-main/.gitignore
+++ b/material/conferences/2024-02-laug-meetup/kmp-wizard-multiplatform-library-template-main/.gitignore
@@ -7,4 +7,5 @@ captures
.externalNativeBuild
.cxx
local.properties
-xcuserdata
\ No newline at end of file
+xcuserdata
+bin
\ No newline at end of file
diff --git a/material/conferences/2024-02-laug-meetup/kotlin2-experimental/app/bin/main/kotlin2/experimental/App.kt b/material/conferences/2024-02-laug-meetup/kotlin2-experimental/app/bin/main/kotlin2/experimental/App.kt
new file mode 100644
index 00000000..7aaa7bcf
--- /dev/null
+++ b/material/conferences/2024-02-laug-meetup/kotlin2-experimental/app/bin/main/kotlin2/experimental/App.kt
@@ -0,0 +1,19 @@
+// source: https://gist.github.com/dellisd/a1e2ae1a7e6b61590bef4b2542a555a0
+
+package kotlin2.experimental
+
+class Test {
+ val names: List
+ field: MutableList = mutableListOf()
+
+ fun doThing() {
+ names.add("Hello!")
+ }
+}
+
+fun main() {
+ println(C().elementList)
+
+ val list = [1, 2, 3]
+ println(list)
+}
diff --git a/material/conferences/2024-02-laug-meetup/kotlin2-experimental/app/bin/test/kotlin2/experimental/AppTest.kt b/material/conferences/2024-02-laug-meetup/kotlin2-experimental/app/bin/test/kotlin2/experimental/AppTest.kt
new file mode 100644
index 00000000..25bb4b5c
--- /dev/null
+++ b/material/conferences/2024-02-laug-meetup/kotlin2-experimental/app/bin/test/kotlin2/experimental/AppTest.kt
@@ -0,0 +1,10 @@
+/*
+ * This Kotlin source file was generated by the Gradle 'init' task.
+ */
+package kotlin2.experimental
+
+import kotlin.test.Test
+
+class AppTest {
+ @Test fun appHasAGreeting() {}
+}
diff --git a/material/rest-api-http4k/.gitignore b/material/rest-api-http4k/.gitignore
new file mode 100644
index 00000000..c426c32f
--- /dev/null
+++ b/material/rest-api-http4k/.gitignore
@@ -0,0 +1,36 @@
+.gradle
+build/
+!gradle/wrapper/gradle-wrapper.jar
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+bin/
+!**/src/main/**/bin/
+!**/src/test/**/bin/
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+out/
+!**/src/main/**/out/
+!**/src/test/**/out/
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### VS Code ###
+.vscode/
\ No newline at end of file
diff --git a/material/rest-api-http4k/README.md b/material/rest-api-http4k/README.md
new file mode 100644
index 00000000..063abc96
--- /dev/null
+++ b/material/rest-api-http4k/README.md
@@ -0,0 +1,7 @@
+# HelloWorld
+
+## Package
+```
+./gradlew distZip
+```
+
diff --git a/material/rest-api-http4k/build.gradle.kts b/material/rest-api-http4k/build.gradle.kts
new file mode 100644
index 00000000..7a5fac8d
--- /dev/null
+++ b/material/rest-api-http4k/build.gradle.kts
@@ -0,0 +1,65 @@
+import org.gradle.api.JavaVersion.VERSION_11
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+plugins {
+ kotlin("jvm") version "1.9.23"
+ application
+}
+
+buildscript {
+ repositories {
+ mavenCentral()
+ gradlePluginPortal()
+ }
+
+ dependencies {
+ }
+}
+
+val http4kVersion: String by project
+val http4kConnectVersion: String by project
+val junitVersion: String by project
+val kotlinVersion: String by project
+
+application {
+ mainClass = "com.worldline.HelloWorldKt"
+}
+
+repositories {
+ mavenCentral()
+}
+
+apply(plugin = "kotlin")
+
+tasks {
+ withType {
+ kotlinOptions {
+ allWarningsAsErrors = false
+ jvmTarget = "11"
+ freeCompilerArgs += "-Xjvm-default=all"
+ }
+ }
+
+ withType {
+ useJUnitPlatform()
+ }
+
+ java {
+ sourceCompatibility = VERSION_11
+ targetCompatibility = VERSION_11
+ }
+}
+
+dependencies {
+ implementation("org.http4k:http4k-client-okhttp:${http4kVersion}")
+ implementation("org.http4k:http4k-core:${http4kVersion}")
+ implementation("org.http4k:http4k-format-kotlinx-serialization:${http4kVersion}")
+ implementation("org.http4k:http4k-server-undertow:${http4kVersion}")
+ implementation("org.http4k:http4k-template-jte:${http4kVersion}")
+ implementation("org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}")
+ testImplementation("org.http4k:http4k-testing-approval:${http4kVersion}")
+ testImplementation("org.http4k:http4k-testing-hamkrest:${http4kVersion}")
+ testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2")
+ testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
+}
+
diff --git a/material/rest-api-http4k/gradle.properties b/material/rest-api-http4k/gradle.properties
new file mode 100644
index 00000000..8e73788d
--- /dev/null
+++ b/material/rest-api-http4k/gradle.properties
@@ -0,0 +1,4 @@
+junitVersion=5.10.2
+http4kVersion=5.14.4.0
+http4kConnectVersion=5.10.1.0
+kotlinVersion=1.9.23
diff --git a/material/rest-api-http4k/gradle/wrapper/gradle-wrapper.jar b/material/rest-api-http4k/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..5c2d1cf0
Binary files /dev/null and b/material/rest-api-http4k/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/material/rest-api-http4k/gradle/wrapper/gradle-wrapper.properties b/material/rest-api-http4k/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..d951fac2
--- /dev/null
+++ b/material/rest-api-http4k/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/material/rest-api-http4k/gradlew b/material/rest-api-http4k/gradlew
new file mode 100644
index 00000000..8e25e6c1
--- /dev/null
+++ b/material/rest-api-http4k/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/material/rest-api-http4k/gradlew.bat b/material/rest-api-http4k/gradlew.bat
new file mode 100644
index 00000000..9618d8d9
--- /dev/null
+++ b/material/rest-api-http4k/gradlew.bat
@@ -0,0 +1,100 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/material/rest-api-http4k/settings.gradle.kts b/material/rest-api-http4k/settings.gradle.kts
new file mode 100644
index 00000000..36055d55
--- /dev/null
+++ b/material/rest-api-http4k/settings.gradle.kts
@@ -0,0 +1 @@
+rootProject.name = "HelloWorld"
diff --git a/material/rest-api-http4k/src/main/kotlin/com/worldline/HelloWorld.kt b/material/rest-api-http4k/src/main/kotlin/com/worldline/HelloWorld.kt
new file mode 100644
index 00000000..059a062e
--- /dev/null
+++ b/material/rest-api-http4k/src/main/kotlin/com/worldline/HelloWorld.kt
@@ -0,0 +1,41 @@
+package com.worldline
+
+import com.worldline.formats.kotlinXMessage
+import com.worldline.formats.kotlinXMessageLens
+import com.worldline.models.JTEViewModel
+import org.http4k.core.*
+import org.http4k.core.ContentType.Companion.TEXT_HTML
+import org.http4k.core.Method.GET
+import org.http4k.core.Status.Companion.OK
+import org.http4k.filter.DebuggingFilters.PrintRequest
+import org.http4k.routing.bind
+import org.http4k.routing.routes
+import org.http4k.server.Undertow
+import org.http4k.server.asServer
+import org.http4k.template.JTETemplates
+import org.http4k.template.viewModel
+
+val app: HttpHandler = routes(
+ "/ping" bind GET to {
+ Response(OK).body("pong")
+ },
+
+ "/formats/json/kotlinx" bind GET to {
+ Response(OK).with(kotlinXMessageLens of kotlinXMessage)
+ },
+
+ "/templates/jte" bind GET to {
+ val renderer = JTETemplates().CachingClasspath()
+ val view = Body.viewModel(renderer, TEXT_HTML).toLens()
+ val viewModel = JTEViewModel("Hello there!")
+ Response(OK).with(view of viewModel)
+ }
+)
+
+fun main() {
+ val printingApp: HttpHandler = PrintRequest().then(app)
+
+ val server = printingApp.asServer(Undertow(3001)).start()
+
+ println("Server started on " + server.port())
+}
diff --git a/material/rest-api-http4k/src/main/kotlin/com/worldline/formats/KotlinXMessage.kt b/material/rest-api-http4k/src/main/kotlin/com/worldline/formats/KotlinXMessage.kt
new file mode 100644
index 00000000..157328fa
--- /dev/null
+++ b/material/rest-api-http4k/src/main/kotlin/com/worldline/formats/KotlinXMessage.kt
@@ -0,0 +1,17 @@
+package com.worldline.formats
+
+import org.http4k.core.Body
+import org.http4k.format.KotlinxSerialization.array
+import org.http4k.format.KotlinxSerialization.boolean
+import org.http4k.format.KotlinxSerialization.json
+import org.http4k.format.KotlinxSerialization.number
+import org.http4k.format.KotlinxSerialization.obj
+import org.http4k.format.KotlinxSerialization.string
+
+val kotlinXMessageLens = Body.json().toLens()
+
+val kotlinXMessage = obj(
+ "thisIsAString" to string("stringValue"),
+ "thisIsANumber" to number(12345),
+ "thisIsAList" to array(listOf(boolean(true)))
+)
diff --git a/material/rest-api-http4k/src/main/kotlin/com/worldline/models/JTEViewModel.kt b/material/rest-api-http4k/src/main/kotlin/com/worldline/models/JTEViewModel.kt
new file mode 100644
index 00000000..e05e5431
--- /dev/null
+++ b/material/rest-api-http4k/src/main/kotlin/com/worldline/models/JTEViewModel.kt
@@ -0,0 +1,5 @@
+package com.worldline.models
+
+import org.http4k.template.ViewModel
+
+data class JTEViewModel(val description: String) : ViewModel
diff --git a/material/rest-api-http4k/src/main/resources/com/worldline/models/JTEViewModel.kte b/material/rest-api-http4k/src/main/resources/com/worldline/models/JTEViewModel.kte
new file mode 100644
index 00000000..d1c671a5
--- /dev/null
+++ b/material/rest-api-http4k/src/main/resources/com/worldline/models/JTEViewModel.kte
@@ -0,0 +1 @@
+${description}
diff --git a/material/rest-api-http4k/src/test/kotlin/com/worldline/HelloWorldClient.kt b/material/rest-api-http4k/src/test/kotlin/com/worldline/HelloWorldClient.kt
new file mode 100644
index 00000000..43bafcd0
--- /dev/null
+++ b/material/rest-api-http4k/src/test/kotlin/com/worldline/HelloWorldClient.kt
@@ -0,0 +1,19 @@
+package com.worldline
+
+import org.http4k.client.OkHttp
+import org.http4k.core.HttpHandler
+import org.http4k.core.Method.GET
+import org.http4k.core.Request
+import org.http4k.core.Response
+import org.http4k.core.then
+import org.http4k.filter.DebuggingFilters.PrintResponse
+
+fun main() {
+ val client: HttpHandler = OkHttp()
+
+ val printingClient: HttpHandler = PrintResponse().then(client)
+
+ val response: Response = printingClient(Request(GET, "http://localhost:9000/ping"))
+
+ println(response.bodyString())
+}
diff --git a/material/rest-api-http4k/src/test/kotlin/com/worldline/HelloWorldTest.kt b/material/rest-api-http4k/src/test/kotlin/com/worldline/HelloWorldTest.kt
new file mode 100644
index 00000000..bd883684
--- /dev/null
+++ b/material/rest-api-http4k/src/test/kotlin/com/worldline/HelloWorldTest.kt
@@ -0,0 +1,17 @@
+package com.worldline
+
+import org.http4k.core.Method.GET
+import org.http4k.core.Request
+import org.http4k.core.Response
+import org.http4k.core.Status.Companion.OK
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
+
+class HelloWorldTest {
+
+ @Test
+ fun `Ping test`() {
+ assertEquals(Response(OK).body("pong"), app(Request(GET, "/ping")))
+ }
+
+}
diff --git a/material/rest-api-ktor-server/gradle/wrapper/gradle-wrapper.properties b/material/rest-api-ktor-server/gradle/wrapper/gradle-wrapper.properties
index ae04661e..48c0a02c 100644
--- a/material/rest-api-ktor-server/gradle/wrapper/gradle-wrapper.properties
+++ b/material/rest-api-ktor-server/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/material/rest-api-spring-kotlin/src/test/kotlin/com/worldline/training/restapispringkotlin/controller/ProductControllerUnitTests.kt b/material/rest-api-spring-kotlin/src/test/kotlin/com/worldline/training/restapispringkotlin/controller/ProductControllerUnitTests.kt
index b6c53536..73047424 100644
--- a/material/rest-api-spring-kotlin/src/test/kotlin/com/worldline/training/restapispringkotlin/controller/ProductControllerUnitTests.kt
+++ b/material/rest-api-spring-kotlin/src/test/kotlin/com/worldline/training/restapispringkotlin/controller/ProductControllerUnitTests.kt
@@ -31,7 +31,11 @@ class ProductControllerUnitTests(@Autowired val mockMvc: MockMvc,
fun testWithClassicApproach(){
mockMvc.perform(get("/product"))
.andExpect(status().isOk)
- .andExpect(content().string(containsString("[]")))
+ .andExpect(
+ content().string(
+ containsString("[]")
+ )
+ )
}
@Test
@@ -41,6 +45,7 @@ class ProductControllerUnitTests(@Autowired val mockMvc: MockMvc,
contentType = MediaType.APPLICATION_JSON
}.andExpect {
status { isCreated() }
+ content { containsString("[]") }
}
mockMvc.get("/product/1").andExpect {
diff --git a/material/samples/app/src/main/java/com/worldline/learningkt/samples/Person.java b/material/samples/app/src/main/java/com/worldline/learningkt/samples/Person.java
index a30ce1e5..6327447f 100644
--- a/material/samples/app/src/main/java/com/worldline/learningkt/samples/Person.java
+++ b/material/samples/app/src/main/java/com/worldline/learningkt/samples/Person.java
@@ -4,26 +4,34 @@
public class Person {
@Nonnull
- private String name;
- public Person(@Nonnull String name) {
- this.name = name;
+ private String name;
+
+ public Person(@Nonnull String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
}
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- @Override public boolean equals(Object o) {
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
// ...
return true;
}
- @Override public int hashCode() {
+
+ @Override
+ public int hashCode() {
// ***
return 0;
}
- @Override public String toString() {
- return ""
+
+ @Override
+ public String toString() {
+ return "";
}
}
\ No newline at end of file
diff --git a/material/samples/app/src/main/kotlin/OtherPerson.kt b/material/samples/app/src/main/kotlin/OtherPerson.kt
index 31388a1a..13b49bc2 100644
--- a/material/samples/app/src/main/kotlin/OtherPerson.kt
+++ b/material/samples/app/src/main/kotlin/OtherPerson.kt
@@ -1,2 +1,2 @@
-data class Person (var name: String)
+data class Person(val name: String)
diff --git a/material/samples/gradle/wrapper/gradle-wrapper.properties b/material/samples/gradle/wrapper/gradle-wrapper.properties
index 3fa8f862..b82aa23a 100644
--- a/material/samples/gradle/wrapper/gradle-wrapper.properties
+++ b/material/samples/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME