├── ktor-2.x ├── settings.gradle.kts ├── .gitignore ├── src │ ├── main │ │ ├── resources │ │ │ ├── static │ │ │ │ └── index.html │ │ │ ├── db │ │ │ │ └── migration │ │ │ │ │ ├── changesets │ │ │ │ │ └── changeset-202102281045.sql │ │ │ │ │ └── migrations.xml │ │ │ ├── application.conf │ │ │ ├── doc │ │ │ │ ├── index.html │ │ │ │ └── swagger.yml │ │ │ ├── swagger │ │ │ │ └── swagger.yml │ │ │ └── logback.xml │ │ └── kotlin │ │ │ └── com │ │ │ └── prof18 │ │ │ └── ktor │ │ │ └── chucknorris │ │ │ └── sample │ │ │ ├── database │ │ │ ├── DatabaseFactory.kt │ │ │ └── DatabaseFactoryImpl.kt │ │ │ ├── features │ │ │ └── jokes │ │ │ │ ├── data │ │ │ │ ├── JokeLocalDataSource.kt │ │ │ │ ├── JokeLocalDataSourceImpl.kt │ │ │ │ └── dao │ │ │ │ │ └── Joke.kt │ │ │ │ ├── domain │ │ │ │ ├── model │ │ │ │ │ └── JokeDTO.kt │ │ │ │ ├── JokeRepository.kt │ │ │ │ └── mapper │ │ │ │ │ └── DTOMapper.kt │ │ │ │ └── resource │ │ │ │ └── JokeResource.kt │ │ │ ├── jobs │ │ │ ├── JobFactory.kt │ │ │ ├── RandomJokeJob.kt │ │ │ └── JobSchedulerManager.kt │ │ │ ├── di │ │ │ └── AppModule.kt │ │ │ └── config │ │ │ └── AppConfig.kt │ └── test │ │ ├── kotlin │ │ └── com │ │ │ └── prof18 │ │ │ └── ktor │ │ │ └── chucknorris │ │ │ └── sample │ │ │ ├── testutils │ │ │ └── database │ │ │ │ ├── SchemaDefinition.kt │ │ │ │ ├── DatabaseFactoryForUnitTest.kt │ │ │ │ └── DatabaseFactoryForServerTest.kt │ │ │ └── features │ │ │ └── jokes │ │ │ └── resource │ │ │ └── JokeResourceTest.kt │ │ └── resources │ │ └── logback-test.xml ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradle.properties ├── local.properties └── README.md ├── part1 ├── settings.gradle.kts ├── .gitignore ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── src │ ├── main │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── prof18 │ │ │ │ └── ktor │ │ │ │ └── chucknorris │ │ │ │ └── sample │ │ │ │ ├── features │ │ │ │ └── jokes │ │ │ │ │ ├── data │ │ │ │ │ ├── dao │ │ │ │ │ │ └── Joke.kt │ │ │ │ │ ├── JokeLocalDataSource.kt │ │ │ │ │ └── JokeLocalDataSourceImpl.kt │ │ │ │ │ ├── domain │ │ │ │ │ ├── mapper │ │ │ │ │ │ └── DTOMapper.kt │ │ │ │ │ ├── model │ │ │ │ │ │ └── JokeDTO.kt │ │ │ │ │ ├── JokeRepository.kt │ │ │ │ │ └── JokeRepositoryImpl.kt │ │ │ │ │ └── resource │ │ │ │ │ └── JokeResource.kt │ │ │ │ ├── database │ │ │ │ ├── DatabaseFactory.kt │ │ │ │ └── DatabaseFactoryImpl.kt │ │ │ │ ├── di │ │ │ │ └── AppModule.kt │ │ │ │ ├── config │ │ │ │ └── AppConfig.kt │ │ │ │ └── Application.kt │ │ └── resources │ │ │ ├── application.conf │ │ │ └── logback.xml │ └── test │ │ └── kotlin │ │ └── com │ │ └── prof18 │ │ └── ktor │ │ └── chucknorris │ │ └── sample │ │ ├── features │ │ └── jokes │ │ │ ├── fake │ │ │ └── JokeRepositoryFake.kt │ │ │ └── resource │ │ │ └── JokeResourceTest.kt │ │ └── testutils │ │ └── TestServer.kt ├── README.md └── build.gradle.kts ├── part2 ├── settings.gradle.kts ├── .gitignore ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── src │ ├── main │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── prof18 │ │ │ │ └── ktor │ │ │ │ └── chucknorris │ │ │ │ └── sample │ │ │ │ ├── features │ │ │ │ └── jokes │ │ │ │ │ ├── data │ │ │ │ │ ├── dao │ │ │ │ │ │ └── Joke.kt │ │ │ │ │ ├── JokeLocalDataSource.kt │ │ │ │ │ └── JokeLocalDataSourceImpl.kt │ │ │ │ │ ├── domain │ │ │ │ │ ├── mapper │ │ │ │ │ │ └── DTOMapper.kt │ │ │ │ │ ├── model │ │ │ │ │ │ └── JokeDTO.kt │ │ │ │ │ ├── JokeRepository.kt │ │ │ │ │ └── JokeRepositoryImpl.kt │ │ │ │ │ └── resource │ │ │ │ │ └── JokeResource.kt │ │ │ │ ├── database │ │ │ │ ├── DatabaseFactory.kt │ │ │ │ └── DatabaseFactoryImpl.kt │ │ │ │ ├── di │ │ │ │ └── AppModule.kt │ │ │ │ ├── config │ │ │ │ └── AppConfig.kt │ │ │ │ └── Application.kt │ │ └── resources │ │ │ ├── application.conf │ │ │ └── logback.xml │ └── test │ │ ├── kotlin │ │ └── com │ │ │ └── prof18 │ │ │ └── ktor │ │ │ └── chucknorris │ │ │ └── sample │ │ │ ├── features │ │ │ └── jokes │ │ │ │ ├── fake │ │ │ │ └── JokeRepositoryFake.kt │ │ │ │ └── resource │ │ │ │ └── JokeResourceTest.kt │ │ │ └── testutils │ │ │ └── TestServer.kt │ │ └── resources │ │ └── logback-test.xml ├── README.md └── build.gradle.kts ├── part3 ├── settings.gradle.kts ├── .gitignore ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── src │ ├── main │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── prof18 │ │ │ │ └── ktor │ │ │ │ └── chucknorris │ │ │ │ └── sample │ │ │ │ ├── database │ │ │ │ ├── DatabaseFactory.kt │ │ │ │ └── DatabaseFactoryImpl.kt │ │ │ │ ├── features │ │ │ │ └── jokes │ │ │ │ │ ├── data │ │ │ │ │ ├── JokeLocalDataSource.kt │ │ │ │ │ ├── JokeLocalDataSourceImpl.kt │ │ │ │ │ └── dao │ │ │ │ │ │ └── Joke.kt │ │ │ │ │ ├── domain │ │ │ │ │ ├── model │ │ │ │ │ │ └── JokeDTO.kt │ │ │ │ │ ├── JokeRepository.kt │ │ │ │ │ ├── mapper │ │ │ │ │ │ └── DTOMapper.kt │ │ │ │ │ └── JokeRepositoryImpl.kt │ │ │ │ │ └── resource │ │ │ │ │ └── JokeResource.kt │ │ │ │ ├── di │ │ │ │ └── AppModule.kt │ │ │ │ ├── config │ │ │ │ └── AppConfig.kt │ │ │ │ └── Application.kt │ │ └── resources │ │ │ ├── db │ │ │ └── migration │ │ │ │ ├── changesets │ │ │ │ └── changeset-202102281045.sql │ │ │ │ └── migrations.xml │ │ │ ├── application.conf │ │ │ └── logback.xml │ └── test │ │ ├── kotlin │ │ └── com │ │ │ └── prof18 │ │ │ └── ktor │ │ │ └── chucknorris │ │ │ └── sample │ │ │ ├── testutils │ │ │ ├── database │ │ │ │ ├── SchemaDefinition.kt │ │ │ │ ├── DatabaseFactoryForUnitTest.kt │ │ │ │ └── DatabaseFactoryForServerTest.kt │ │ │ └── TestServer.kt │ │ │ └── features │ │ │ └── jokes │ │ │ ├── domain │ │ │ └── JokeRepositoryImplTest.kt │ │ │ └── resource │ │ │ └── JokeResourceTest.kt │ │ └── resources │ │ └── logback-test.xml ├── gradle.properties ├── local.properties ├── README.md └── build.gradle.kts ├── part4 ├── settings.gradle.kts ├── .gitignore ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── src │ ├── main │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── prof18 │ │ │ │ └── ktor │ │ │ │ └── chucknorris │ │ │ │ └── sample │ │ │ │ ├── database │ │ │ │ ├── DatabaseFactory.kt │ │ │ │ └── DatabaseFactoryImpl.kt │ │ │ │ ├── features │ │ │ │ └── jokes │ │ │ │ │ ├── data │ │ │ │ │ ├── JokeLocalDataSource.kt │ │ │ │ │ ├── JokeLocalDataSourceImpl.kt │ │ │ │ │ └── dao │ │ │ │ │ │ └── Joke.kt │ │ │ │ │ ├── domain │ │ │ │ │ ├── model │ │ │ │ │ │ └── JokeDTO.kt │ │ │ │ │ ├── JokeRepository.kt │ │ │ │ │ ├── mapper │ │ │ │ │ │ └── DTOMapper.kt │ │ │ │ │ └── JokeRepositoryImpl.kt │ │ │ │ │ └── resource │ │ │ │ │ └── JokeResource.kt │ │ │ │ ├── di │ │ │ │ └── AppModule.kt │ │ │ │ ├── config │ │ │ │ └── AppConfig.kt │ │ │ │ └── Application.kt │ │ └── resources │ │ │ ├── db │ │ │ └── migration │ │ │ │ ├── changesets │ │ │ │ └── changeset-202102281045.sql │ │ │ │ └── migrations.xml │ │ │ ├── application.conf │ │ │ └── logback.xml │ └── test │ │ ├── kotlin │ │ └── com │ │ │ └── prof18 │ │ │ └── ktor │ │ │ └── chucknorris │ │ │ └── sample │ │ │ ├── testutils │ │ │ ├── database │ │ │ │ ├── SchemaDefinition.kt │ │ │ │ ├── DatabaseFactoryForUnitTest.kt │ │ │ │ └── DatabaseFactoryForServerTest.kt │ │ │ └── TestServer.kt │ │ │ └── features │ │ │ └── jokes │ │ │ ├── domain │ │ │ └── JokeRepositoryImplTest.kt │ │ │ └── resource │ │ │ └── JokeResourceTest.kt │ │ └── resources │ │ └── logback-test.xml ├── gradle.properties ├── local.properties └── README.md ├── part5 ├── settings.gradle.kts ├── .gitignore ├── src │ ├── main │ │ ├── resources │ │ │ ├── static │ │ │ │ └── index.html │ │ │ ├── db │ │ │ │ └── migration │ │ │ │ │ ├── changesets │ │ │ │ │ └── changeset-202102281045.sql │ │ │ │ │ └── migrations.xml │ │ │ ├── application.conf │ │ │ ├── doc │ │ │ │ ├── index.html │ │ │ │ └── swagger.yml │ │ │ ├── swagger │ │ │ │ └── swagger.yml │ │ │ └── logback.xml │ │ └── kotlin │ │ │ └── com │ │ │ └── prof18 │ │ │ └── ktor │ │ │ └── chucknorris │ │ │ └── sample │ │ │ ├── database │ │ │ ├── DatabaseFactory.kt │ │ │ └── DatabaseFactoryImpl.kt │ │ │ ├── features │ │ │ └── jokes │ │ │ │ ├── data │ │ │ │ ├── JokeLocalDataSource.kt │ │ │ │ ├── JokeLocalDataSourceImpl.kt │ │ │ │ └── dao │ │ │ │ │ └── Joke.kt │ │ │ │ ├── domain │ │ │ │ ├── model │ │ │ │ │ └── JokeDTO.kt │ │ │ │ ├── JokeRepository.kt │ │ │ │ ├── mapper │ │ │ │ │ └── DTOMapper.kt │ │ │ │ └── JokeRepositoryImpl.kt │ │ │ │ └── resource │ │ │ │ └── JokeResource.kt │ │ │ ├── di │ │ │ └── AppModule.kt │ │ │ └── config │ │ │ └── AppConfig.kt │ └── test │ │ ├── kotlin │ │ └── com │ │ │ └── prof18 │ │ │ └── ktor │ │ │ └── chucknorris │ │ │ └── sample │ │ │ ├── testutils │ │ │ ├── database │ │ │ │ ├── SchemaDefinition.kt │ │ │ │ ├── DatabaseFactoryForUnitTest.kt │ │ │ │ └── DatabaseFactoryForServerTest.kt │ │ │ └── TestServer.kt │ │ │ └── features │ │ │ └── jokes │ │ │ ├── domain │ │ │ └── JokeRepositoryImplTest.kt │ │ │ └── resource │ │ │ └── JokeResourceTest.kt │ │ └── resources │ │ └── logback-test.xml ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradle.properties ├── local.properties └── README.md ├── part6 ├── settings.gradle.kts ├── .gitignore ├── src │ ├── main │ │ ├── resources │ │ │ ├── static │ │ │ │ └── index.html │ │ │ ├── db │ │ │ │ └── migration │ │ │ │ │ ├── changesets │ │ │ │ │ └── changeset-202102281045.sql │ │ │ │ │ └── migrations.xml │ │ │ ├── application.conf │ │ │ ├── doc │ │ │ │ ├── index.html │ │ │ │ └── swagger.yml │ │ │ ├── swagger │ │ │ │ └── swagger.yml │ │ │ └── logback.xml │ │ └── kotlin │ │ │ └── com │ │ │ └── prof18 │ │ │ └── ktor │ │ │ └── chucknorris │ │ │ └── sample │ │ │ ├── database │ │ │ ├── DatabaseFactory.kt │ │ │ └── DatabaseFactoryImpl.kt │ │ │ ├── features │ │ │ └── jokes │ │ │ │ ├── data │ │ │ │ ├── JokeLocalDataSource.kt │ │ │ │ ├── JokeLocalDataSourceImpl.kt │ │ │ │ └── dao │ │ │ │ │ └── Joke.kt │ │ │ │ ├── domain │ │ │ │ ├── model │ │ │ │ │ └── JokeDTO.kt │ │ │ │ ├── JokeRepository.kt │ │ │ │ ├── mapper │ │ │ │ │ └── DTOMapper.kt │ │ │ │ └── JokeRepositoryImpl.kt │ │ │ │ └── resource │ │ │ │ └── JokeResource.kt │ │ │ ├── jobs │ │ │ ├── JobFactory.kt │ │ │ ├── RandomJokeJob.kt │ │ │ └── JobSchedulerManager.kt │ │ │ ├── di │ │ │ └── AppModule.kt │ │ │ └── config │ │ │ └── AppConfig.kt │ └── test │ │ ├── kotlin │ │ └── com │ │ │ └── prof18 │ │ │ └── ktor │ │ │ └── chucknorris │ │ │ └── sample │ │ │ └── testutils │ │ │ └── database │ │ │ ├── SchemaDefinition.kt │ │ │ ├── DatabaseFactoryForUnitTest.kt │ │ │ └── DatabaseFactoryForServerTest.kt │ │ └── resources │ │ └── logback-test.xml ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradle.properties ├── local.properties └── README.md ├── ktor-1.6.x ├── settings.gradle.kts ├── .gitignore ├── src │ ├── main │ │ ├── resources │ │ │ ├── static │ │ │ │ └── index.html │ │ │ ├── db │ │ │ │ └── migration │ │ │ │ │ ├── changesets │ │ │ │ │ └── changeset-202102281045.sql │ │ │ │ │ └── migrations.xml │ │ │ ├── application.conf │ │ │ ├── doc │ │ │ │ ├── index.html │ │ │ │ └── swagger.yml │ │ │ ├── swagger │ │ │ │ └── swagger.yml │ │ │ └── logback.xml │ │ └── kotlin │ │ │ └── com │ │ │ └── prof18 │ │ │ └── ktor │ │ │ └── chucknorris │ │ │ └── sample │ │ │ ├── database │ │ │ ├── DatabaseFactory.kt │ │ │ └── DatabaseFactoryImpl.kt │ │ │ ├── features │ │ │ └── jokes │ │ │ │ ├── data │ │ │ │ ├── JokeLocalDataSource.kt │ │ │ │ ├── JokeLocalDataSourceImpl.kt │ │ │ │ └── dao │ │ │ │ │ └── Joke.kt │ │ │ │ ├── domain │ │ │ │ ├── model │ │ │ │ │ └── JokeDTO.kt │ │ │ │ ├── JokeRepository.kt │ │ │ │ ├── mapper │ │ │ │ │ └── DTOMapper.kt │ │ │ │ └── JokeRepositoryImpl.kt │ │ │ │ └── resource │ │ │ │ └── JokeResource.kt │ │ │ ├── jobs │ │ │ ├── JobFactory.kt │ │ │ ├── RandomJokeJob.kt │ │ │ └── JobSchedulerManager.kt │ │ │ ├── di │ │ │ └── AppModule.kt │ │ │ └── config │ │ │ └── AppConfig.kt │ └── test │ │ ├── kotlin │ │ └── com │ │ │ └── prof18 │ │ │ └── ktor │ │ │ └── chucknorris │ │ │ └── sample │ │ │ └── testutils │ │ │ └── database │ │ │ ├── SchemaDefinition.kt │ │ │ ├── DatabaseFactoryForUnitTest.kt │ │ │ └── DatabaseFactoryForServerTest.kt │ │ └── resources │ │ └── logback-test.xml ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradle.properties ├── local.properties └── README.md ├── .gitignore └── README.md /ktor-2.x/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "ktor-chuck-norris-sample" -------------------------------------------------------------------------------- /part1/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "ktor-chuck-norris-sample" -------------------------------------------------------------------------------- /part2/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "ktor-chuck-norris-sample" -------------------------------------------------------------------------------- /part3/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "ktor-chuck-norris-sample" -------------------------------------------------------------------------------- /part4/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "ktor-chuck-norris-sample" -------------------------------------------------------------------------------- /part5/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "ktor-chuck-norris-sample" -------------------------------------------------------------------------------- /part6/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "ktor-chuck-norris-sample" -------------------------------------------------------------------------------- /ktor-1.6.x/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "ktor-chuck-norris-sample" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle 2 | /.idea 3 | /out 4 | /build 5 | *.iml 6 | *.ipr 7 | *.iws 8 | .DS_Store 9 | -------------------------------------------------------------------------------- /ktor-2.x/.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle 2 | /.idea 3 | /out 4 | /build 5 | *.iml 6 | *.ipr 7 | *.iws 8 | .DS_Store 9 | -------------------------------------------------------------------------------- /part1/.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle 2 | /.idea 3 | /out 4 | /build 5 | *.iml 6 | *.ipr 7 | *.iws 8 | .DS_Store 9 | -------------------------------------------------------------------------------- /part2/.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle 2 | /.idea 3 | /out 4 | /build 5 | *.iml 6 | *.ipr 7 | *.iws 8 | .DS_Store 9 | -------------------------------------------------------------------------------- /part3/.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle 2 | /.idea 3 | /out 4 | /build 5 | *.iml 6 | *.ipr 7 | *.iws 8 | .DS_Store 9 | -------------------------------------------------------------------------------- /part4/.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle 2 | /.idea 3 | /out 4 | /build 5 | *.iml 6 | *.ipr 7 | *.iws 8 | .DS_Store 9 | -------------------------------------------------------------------------------- /part5/.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle 2 | /.idea 3 | /out 4 | /build 5 | *.iml 6 | *.ipr 7 | *.iws 8 | .DS_Store 9 | -------------------------------------------------------------------------------- /part6/.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle 2 | /.idea 3 | /out 4 | /build 5 | *.iml 6 | *.ipr 7 | *.iws 8 | .DS_Store 9 | -------------------------------------------------------------------------------- /ktor-1.6.x/.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle 2 | /.idea 3 | /out 4 | /build 5 | *.iml 6 | *.ipr 7 | *.iws 8 | .DS_Store 9 | -------------------------------------------------------------------------------- /part5/src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

Hello Ktor!

6 | 7 | 8 | -------------------------------------------------------------------------------- /part6/src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

Hello Ktor!

6 | 7 | 8 | -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

Hello Ktor!

6 | 7 | 8 | -------------------------------------------------------------------------------- /ktor-2.x/src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

Hello Ktor!

6 | 7 | 8 | -------------------------------------------------------------------------------- /part1/gradle.properties: -------------------------------------------------------------------------------- 1 | ktor_version=1.6.3 2 | kotlin_version=1.5.30 3 | logback_version=1.2.6 4 | kotlin.code.style=official 5 | koin_version=3.1.2 -------------------------------------------------------------------------------- /part1/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof18/ktor-chuck-norris-sample/HEAD/part1/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part2/gradle.properties: -------------------------------------------------------------------------------- 1 | ktor_version=1.6.3 2 | kotlin_version=1.5.30 3 | logback_version=1.2.6 4 | kotlin.code.style=official 5 | koin_version=3.1.2 -------------------------------------------------------------------------------- /part2/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof18/ktor-chuck-norris-sample/HEAD/part2/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part3/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof18/ktor-chuck-norris-sample/HEAD/part3/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part4/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof18/ktor-chuck-norris-sample/HEAD/part4/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part5/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof18/ktor-chuck-norris-sample/HEAD/part5/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part6/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof18/ktor-chuck-norris-sample/HEAD/part6/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /ktor-2.x/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof18/ktor-chuck-norris-sample/HEAD/ktor-2.x/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /ktor-1.6.x/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prof18/ktor-chuck-norris-sample/HEAD/ktor-1.6.x/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /part1/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/dao/Joke.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data.dao 2 | 3 | 4 | 5 | // TODO -------------------------------------------------------------------------------- /part2/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/dao/Joke.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data.dao 2 | 3 | 4 | 5 | // TODO -------------------------------------------------------------------------------- /part1/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/mapper/DTOMapper.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain.mapper 2 | 3 | 4 | // TODO -------------------------------------------------------------------------------- /part2/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/mapper/DTOMapper.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain.mapper 2 | 3 | 4 | // TODO -------------------------------------------------------------------------------- /part1/src/main/kotlin/com/prof18/ktor/chucknorris/sample/database/DatabaseFactory.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.database 2 | 3 | interface DatabaseFactory { 4 | // TODO 5 | } -------------------------------------------------------------------------------- /part2/src/main/kotlin/com/prof18/ktor/chucknorris/sample/database/DatabaseFactory.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.database 2 | 3 | interface DatabaseFactory { 4 | // TODO 5 | } -------------------------------------------------------------------------------- /ktor-2.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/database/DatabaseFactory.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.database 2 | 3 | interface DatabaseFactory { 4 | fun connect() 5 | fun close() 6 | } -------------------------------------------------------------------------------- /part1/src/main/kotlin/com/prof18/ktor/chucknorris/sample/database/DatabaseFactoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.database 2 | 3 | class DatabaseFactoryImpl : DatabaseFactory { 4 | // TODO 5 | } 6 | -------------------------------------------------------------------------------- /part2/src/main/kotlin/com/prof18/ktor/chucknorris/sample/database/DatabaseFactoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.database 2 | 3 | class DatabaseFactoryImpl : DatabaseFactory { 4 | // TODO 5 | } 6 | -------------------------------------------------------------------------------- /part3/src/main/kotlin/com/prof18/ktor/chucknorris/sample/database/DatabaseFactory.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.database 2 | 3 | interface DatabaseFactory { 4 | fun connect() 5 | fun close() 6 | } -------------------------------------------------------------------------------- /part4/src/main/kotlin/com/prof18/ktor/chucknorris/sample/database/DatabaseFactory.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.database 2 | 3 | interface DatabaseFactory { 4 | fun connect() 5 | fun close() 6 | } -------------------------------------------------------------------------------- /part5/src/main/kotlin/com/prof18/ktor/chucknorris/sample/database/DatabaseFactory.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.database 2 | 3 | interface DatabaseFactory { 4 | fun connect() 5 | fun close() 6 | } -------------------------------------------------------------------------------- /part6/src/main/kotlin/com/prof18/ktor/chucknorris/sample/database/DatabaseFactory.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.database 2 | 3 | interface DatabaseFactory { 4 | fun connect() 5 | fun close() 6 | } -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/database/DatabaseFactory.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.database 2 | 3 | interface DatabaseFactory { 4 | fun connect() 5 | fun close() 6 | } -------------------------------------------------------------------------------- /part1/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/JokeLocalDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data 2 | 3 | interface JokeLocalDataSource { 4 | // TODO 5 | } -------------------------------------------------------------------------------- /part2/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/JokeLocalDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data 2 | 3 | interface JokeLocalDataSource { 4 | // TODO 5 | } -------------------------------------------------------------------------------- /part1/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/JokeLocalDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data 2 | 3 | class JokeLocalDataSourceImpl : JokeLocalDataSource { 4 | // TODO 5 | } -------------------------------------------------------------------------------- /part2/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/JokeLocalDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data 2 | 3 | class JokeLocalDataSourceImpl : JokeLocalDataSource { 4 | // TODO 5 | } -------------------------------------------------------------------------------- /part3/gradle.properties: -------------------------------------------------------------------------------- 1 | ktor_version=1.6.3 2 | kotlin_version=1.5.30 3 | logback_version=1.2.6 4 | kotlin.code.style=official 5 | exposed_version=0.35.1 6 | hikaricp_version=5.0.0 7 | mysql_connector_version=8.0.25 8 | h2_version=1.4.200 9 | koin_version=3.1.2 -------------------------------------------------------------------------------- /part1/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part2/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part3/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part4/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part5/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part6/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /ktor-1.6.x/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /ktor-2.x/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /part1/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | ktor { 2 | deployment { 3 | port = 8080 4 | port = ${?PORT} 5 | } 6 | application { 7 | modules = [com.prof18.ktor.chucknorris.sample.ApplicationKt.module] 8 | } 9 | 10 | server { 11 | isProd = false 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /part2/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | ktor { 2 | deployment { 3 | port = 8080 4 | port = ${?PORT} 5 | } 6 | application { 7 | modules = [com.prof18.ktor.chucknorris.sample.ApplicationKt.module] 8 | } 9 | 10 | server { 11 | isProd = false 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /part4/gradle.properties: -------------------------------------------------------------------------------- 1 | ktor_version=1.6.3 2 | kotlin_version=1.5.30 3 | logback_version=1.2.6 4 | kotlin.code.style=official 5 | exposed_version=0.35.1 6 | hikaricp_version=5.0.0 7 | mysql_connector_version=8.0.25 8 | h2_version=1.4.200 9 | liquibase_core=4.4.3 10 | koin_version=3.1.2 11 | dbEnv= -------------------------------------------------------------------------------- /part5/gradle.properties: -------------------------------------------------------------------------------- 1 | ktor_version=1.6.3 2 | kotlin_version=1.5.30 3 | logback_version=1.2.6 4 | kotlin.code.style=official 5 | exposed_version=0.35.1 6 | hikaricp_version=5.0.0 7 | mysql_connector_version=8.0.25 8 | h2_version=1.4.200 9 | liquibase_core=4.4.3 10 | koin_version=3.1.2 11 | dbEnv= -------------------------------------------------------------------------------- /part6/gradle.properties: -------------------------------------------------------------------------------- 1 | ktor_version=1.6.3 2 | kotlin_version=1.5.30 3 | logback_version=1.2.6 4 | kotlin.code.style=official 5 | exposed_version=0.35.1 6 | hikaricp_version=5.0.0 7 | mysql_connector_version=8.0.25 8 | h2_version=1.4.200 9 | liquibase_core=4.4.3 10 | koin_version=3.1.2 11 | dbEnv= -------------------------------------------------------------------------------- /ktor-1.6.x/gradle.properties: -------------------------------------------------------------------------------- 1 | ktor_version=1.6.3 2 | kotlin_version=1.5.30 3 | logback_version=1.2.6 4 | kotlin.code.style=official 5 | exposed_version=0.35.1 6 | hikaricp_version=5.0.0 7 | mysql_connector_version=8.0.25 8 | h2_version=1.4.200 9 | liquibase_core=4.4.3 10 | koin_version=3.1.2 11 | dbEnv= -------------------------------------------------------------------------------- /ktor-2.x/gradle.properties: -------------------------------------------------------------------------------- 1 | ktor_version=2.3.0 2 | kotlin_version=1.8.20 3 | logback_version=1.4.7 4 | kotlin.code.style=official 5 | exposed_version=0.41.1 6 | hikaricp_version=5.0.1 7 | mysql_connector_version=8.0.33 8 | h2_version=2.1.214 9 | liquibase_core=4.21.1 10 | koin_version=3.4.0 11 | dbEnv= -------------------------------------------------------------------------------- /part3/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/JokeLocalDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | 5 | interface JokeLocalDataSource { 6 | fun getAllJokes(): List 7 | } -------------------------------------------------------------------------------- /part4/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/JokeLocalDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | 5 | interface JokeLocalDataSource { 6 | fun getAllJokes(): List 7 | } -------------------------------------------------------------------------------- /part5/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/JokeLocalDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | 5 | interface JokeLocalDataSource { 6 | fun getAllJokes(): List 7 | } -------------------------------------------------------------------------------- /part6/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/JokeLocalDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | 5 | interface JokeLocalDataSource { 6 | fun getAllJokes(): List 7 | } -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/JokeLocalDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | 5 | interface JokeLocalDataSource { 6 | fun getAllJokes(): List 7 | } -------------------------------------------------------------------------------- /ktor-2.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/JokeLocalDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | 5 | interface JokeLocalDataSource { 6 | fun getAllJokes(): List 7 | } -------------------------------------------------------------------------------- /ktor-2.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/model/JokeDTO.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain.model 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class JokeDTO( 7 | val jokeId: String, 8 | val jokeContent: String 9 | ) -------------------------------------------------------------------------------- /part1/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/model/JokeDTO.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain.model 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class JokeDTO( 7 | val jokeId: String, 8 | val jokeContent: String 9 | ) -------------------------------------------------------------------------------- /part2/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/model/JokeDTO.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain.model 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class JokeDTO( 7 | val jokeId: String, 8 | val jokeContent: String 9 | ) -------------------------------------------------------------------------------- /part3/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/model/JokeDTO.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain.model 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class JokeDTO( 7 | val jokeId: String, 8 | val jokeContent: String 9 | ) -------------------------------------------------------------------------------- /part4/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/model/JokeDTO.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain.model 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class JokeDTO( 7 | val jokeId: String, 8 | val jokeContent: String 9 | ) -------------------------------------------------------------------------------- /part5/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/model/JokeDTO.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain.model 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class JokeDTO( 7 | val jokeId: String, 8 | val jokeContent: String 9 | ) -------------------------------------------------------------------------------- /part6/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/model/JokeDTO.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain.model 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class JokeDTO( 7 | val jokeId: String, 8 | val jokeContent: String 9 | ) -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/model/JokeDTO.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain.model 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class JokeDTO( 7 | val jokeId: String, 8 | val jokeContent: String 9 | ) -------------------------------------------------------------------------------- /part1/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/JokeRepository.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 4 | 5 | interface JokeRepository { 6 | suspend fun getRandomJoke(): JokeDTO 7 | } -------------------------------------------------------------------------------- /part2/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/JokeRepository.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 4 | 5 | interface JokeRepository { 6 | suspend fun getRandomJoke(): JokeDTO 7 | } -------------------------------------------------------------------------------- /part3/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/JokeRepository.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 4 | 5 | interface JokeRepository { 6 | suspend fun getRandomJoke(): JokeDTO 7 | } -------------------------------------------------------------------------------- /part4/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/JokeRepository.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 4 | 5 | interface JokeRepository { 6 | suspend fun getRandomJoke(): JokeDTO 7 | } -------------------------------------------------------------------------------- /part5/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/JokeRepository.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 4 | 5 | interface JokeRepository { 6 | suspend fun getRandomJoke(): JokeDTO 7 | } -------------------------------------------------------------------------------- /part3/src/main/resources/db/migration/changesets/changeset-202102281045.sql: -------------------------------------------------------------------------------- 1 | # From https://github.com/chucknorris-io/chuck-db 2 | 3 | # Joke table 4 | CREATE TABLE IF NOT EXISTS joke 5 | ( 6 | created_at TIMESTAMP NOT NULL , 7 | joke_id VARCHAR(255) PRIMARY KEY, 8 | updated_at TIMESTAMP NOT NULL , 9 | value TEXT NOT NULL 10 | ); -------------------------------------------------------------------------------- /part4/src/main/resources/db/migration/changesets/changeset-202102281045.sql: -------------------------------------------------------------------------------- 1 | # From https://github.com/chucknorris-io/chuck-db 2 | 3 | # Joke table 4 | CREATE TABLE IF NOT EXISTS joke 5 | ( 6 | created_at TIMESTAMP NOT NULL , 7 | joke_id VARCHAR(255) PRIMARY KEY, 8 | updated_at TIMESTAMP NOT NULL , 9 | value TEXT NOT NULL 10 | ); -------------------------------------------------------------------------------- /part5/src/main/resources/db/migration/changesets/changeset-202102281045.sql: -------------------------------------------------------------------------------- 1 | # From https://github.com/chucknorris-io/chuck-db 2 | 3 | # Joke table 4 | CREATE TABLE IF NOT EXISTS joke 5 | ( 6 | created_at TIMESTAMP NOT NULL , 7 | joke_id VARCHAR(255) PRIMARY KEY, 8 | updated_at TIMESTAMP NOT NULL , 9 | value TEXT NOT NULL 10 | ); -------------------------------------------------------------------------------- /part6/src/main/resources/db/migration/changesets/changeset-202102281045.sql: -------------------------------------------------------------------------------- 1 | # From https://github.com/chucknorris-io/chuck-db 2 | 3 | # Joke table 4 | CREATE TABLE IF NOT EXISTS joke 5 | ( 6 | created_at TIMESTAMP NOT NULL , 7 | joke_id VARCHAR(255) PRIMARY KEY, 8 | updated_at TIMESTAMP NOT NULL , 9 | value TEXT NOT NULL 10 | ); -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/resources/db/migration/changesets/changeset-202102281045.sql: -------------------------------------------------------------------------------- 1 | # From https://github.com/chucknorris-io/chuck-db 2 | 3 | # Joke table 4 | CREATE TABLE IF NOT EXISTS joke 5 | ( 6 | created_at TIMESTAMP NOT NULL , 7 | joke_id VARCHAR(255) PRIMARY KEY, 8 | updated_at TIMESTAMP NOT NULL , 9 | value TEXT NOT NULL 10 | ); -------------------------------------------------------------------------------- /ktor-2.x/src/main/resources/db/migration/changesets/changeset-202102281045.sql: -------------------------------------------------------------------------------- 1 | # From https://github.com/chucknorris-io/chuck-db 2 | 3 | # Joke table 4 | CREATE TABLE IF NOT EXISTS joke 5 | ( 6 | created_at TIMESTAMP NOT NULL , 7 | joke_id VARCHAR(255) PRIMARY KEY, 8 | updated_at TIMESTAMP NOT NULL , 9 | value TEXT NOT NULL 10 | ); -------------------------------------------------------------------------------- /part3/local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Sat May 06 16:16:11 CEST 2023 8 | sdk.dir=/Users/mg/Library/Android/sdk 9 | -------------------------------------------------------------------------------- /ktor-2.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/JokeRepository.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 4 | 5 | interface JokeRepository { 6 | suspend fun getRandomJoke(): JokeDTO 7 | suspend fun watch(name: String) 8 | fun getChuckGreeting(name: String): String 9 | } -------------------------------------------------------------------------------- /part6/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/JokeRepository.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 4 | 5 | interface JokeRepository { 6 | suspend fun getRandomJoke(): JokeDTO 7 | suspend fun watch(name: String) 8 | fun getChuckGreeting(name: String): String 9 | } -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/JokeRepository.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 4 | 5 | interface JokeRepository { 6 | suspend fun getRandomJoke(): JokeDTO 7 | suspend fun watch(name: String) 8 | fun getChuckGreeting(name: String): String 9 | } -------------------------------------------------------------------------------- /part3/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/mapper/DTOMapper.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain.mapper 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 5 | 6 | fun Joke.toDTO(): JokeDTO { 7 | return JokeDTO( 8 | jokeId = this.id.value, 9 | jokeContent = this.value 10 | ) 11 | } -------------------------------------------------------------------------------- /part4/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/mapper/DTOMapper.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain.mapper 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 5 | 6 | fun Joke.toDTO(): JokeDTO { 7 | return JokeDTO( 8 | jokeId = this.id.value, 9 | jokeContent = this.value 10 | ) 11 | } -------------------------------------------------------------------------------- /part5/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/mapper/DTOMapper.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain.mapper 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 5 | 6 | fun Joke.toDTO(): JokeDTO { 7 | return JokeDTO( 8 | jokeId = this.id.value, 9 | jokeContent = this.value 10 | ) 11 | } -------------------------------------------------------------------------------- /part6/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/mapper/DTOMapper.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain.mapper 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 5 | 6 | fun Joke.toDTO(): JokeDTO { 7 | return JokeDTO( 8 | jokeId = this.id.value, 9 | jokeContent = this.value 10 | ) 11 | } -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/mapper/DTOMapper.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain.mapper 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 5 | 6 | fun Joke.toDTO(): JokeDTO { 7 | return JokeDTO( 8 | jokeId = this.id.value, 9 | jokeContent = this.value 10 | ) 11 | } -------------------------------------------------------------------------------- /ktor-2.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/mapper/DTOMapper.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain.mapper 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 5 | 6 | fun Joke.toDTO(): JokeDTO { 7 | return JokeDTO( 8 | jokeId = this.id.value, 9 | jokeContent = this.value 10 | ) 11 | } -------------------------------------------------------------------------------- /part4/local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Sat May 06 16:17:57 CEST 2023 8 | liquibase.pwd=password 9 | liquibase.url=jdbc\:mysql\://your-url.com 10 | liquibase.user=user 11 | sdk.dir=/Users/mg/Library/Android/sdk 12 | -------------------------------------------------------------------------------- /part5/local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Sat May 06 16:19:35 CEST 2023 8 | liquibase.pwd=password 9 | liquibase.url=jdbc\:mysql\://your-url.com 10 | liquibase.user=user 11 | sdk.dir=/Users/mg/Library/Android/sdk 12 | -------------------------------------------------------------------------------- /part6/local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Sat May 06 16:22:20 CEST 2023 8 | liquibase.pwd=password 9 | liquibase.url=jdbc\:mysql\://your-url.com 10 | liquibase.user=user 11 | sdk.dir=/Users/mg/Library/Android/sdk 12 | -------------------------------------------------------------------------------- /ktor-1.6.x/local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Sat May 06 16:28:17 CEST 2023 8 | liquibase.pwd=password 9 | liquibase.url=jdbc\:mysql\://your-url.com 10 | liquibase.user=user 11 | sdk.dir=/Users/mg/Library/Android/sdk 12 | -------------------------------------------------------------------------------- /ktor-2.x/local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Sat May 06 16:28:17 CEST 2023 8 | liquibase.pwd=password 9 | liquibase.url=jdbc\:mysql\://your-url.com 10 | liquibase.user=user 11 | sdk.dir=/Users/mg/Library/Android/sdk 12 | -------------------------------------------------------------------------------- /part1/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /part1/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/JokeRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 4 | 5 | class JokeRepositoryImpl : JokeRepository { 6 | 7 | override suspend fun getRandomJoke(): JokeDTO { 8 | // TODO 9 | return JokeDTO( 10 | jokeId = "joke-id", 11 | jokeContent = "joke-content" 12 | ) 13 | } 14 | } -------------------------------------------------------------------------------- /part2/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/JokeRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 4 | 5 | class JokeRepositoryImpl : JokeRepository { 6 | 7 | override suspend fun getRandomJoke(): JokeDTO { 8 | // TODO 9 | return JokeDTO( 10 | jokeId = "joke-id", 11 | jokeContent = "joke-content" 12 | ) 13 | } 14 | } -------------------------------------------------------------------------------- /part3/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/database/SchemaDefinition.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.JokeTable 4 | import org.jetbrains.exposed.sql.SchemaUtils 5 | import org.jetbrains.exposed.sql.transactions.transaction 6 | 7 | object SchemaDefinition { 8 | 9 | fun createSchema() { 10 | transaction { 11 | SchemaUtils.create(JokeTable) 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /part4/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/database/SchemaDefinition.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.JokeTable 4 | import org.jetbrains.exposed.sql.SchemaUtils 5 | import org.jetbrains.exposed.sql.transactions.transaction 6 | 7 | object SchemaDefinition { 8 | 9 | fun createSchema() { 10 | transaction { 11 | SchemaUtils.create(JokeTable) 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /part5/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/database/SchemaDefinition.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.JokeTable 4 | import org.jetbrains.exposed.sql.SchemaUtils 5 | import org.jetbrains.exposed.sql.transactions.transaction 6 | 7 | object SchemaDefinition { 8 | 9 | fun createSchema() { 10 | transaction { 11 | SchemaUtils.create(JokeTable) 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /part6/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/database/SchemaDefinition.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.JokeTable 4 | import org.jetbrains.exposed.sql.SchemaUtils 5 | import org.jetbrains.exposed.sql.transactions.transaction 6 | 7 | object SchemaDefinition { 8 | 9 | fun createSchema() { 10 | transaction { 11 | SchemaUtils.create(JokeTable) 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /ktor-1.6.x/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/database/SchemaDefinition.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.JokeTable 4 | import org.jetbrains.exposed.sql.SchemaUtils 5 | import org.jetbrains.exposed.sql.transactions.transaction 6 | 7 | object SchemaDefinition { 8 | 9 | fun createSchema() { 10 | transaction { 11 | SchemaUtils.create(JokeTable) 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /ktor-2.x/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/database/SchemaDefinition.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.JokeTable 4 | import org.jetbrains.exposed.sql.SchemaUtils 5 | import org.jetbrains.exposed.sql.transactions.transaction 6 | 7 | object SchemaDefinition { 8 | 9 | fun createSchema() { 10 | transaction { 11 | SchemaUtils.create(JokeTable) 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /part3/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | ktor { 2 | deployment { 3 | port = 8080 4 | port = ${?PORT} 5 | } 6 | application { 7 | modules = [com.prof18.ktor.chucknorris.sample.ApplicationKt.module] 8 | } 9 | 10 | server { 11 | isProd = false 12 | } 13 | 14 | database { 15 | driverClass = "com.mysql.cj.jdbc.Driver" 16 | url = "jdbc:mysql://localhost:3308/chucknorris?useUnicode=true&characterEncoding=UTF-8" 17 | user = "root" 18 | password = "password" 19 | maxPoolSize = 3 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /part4/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | ktor { 2 | deployment { 3 | port = 8080 4 | port = ${?PORT} 5 | } 6 | application { 7 | modules = [com.prof18.ktor.chucknorris.sample.ApplicationKt.module] 8 | } 9 | 10 | server { 11 | isProd = false 12 | } 13 | 14 | database { 15 | driverClass = "com.mysql.cj.jdbc.Driver" 16 | url = "jdbc:mysql://localhost:3308/chucknorris?useUnicode=true&characterEncoding=UTF-8" 17 | user = "root" 18 | password = "password" 19 | maxPoolSize = 3 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /part5/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | ktor { 2 | deployment { 3 | port = 8080 4 | port = ${?PORT} 5 | } 6 | application { 7 | modules = [com.prof18.ktor.chucknorris.sample.ApplicationKt.module] 8 | } 9 | 10 | server { 11 | isProd = false 12 | } 13 | 14 | database { 15 | driverClass = "com.mysql.cj.jdbc.Driver" 16 | url = "jdbc:mysql://localhost:3308/chucknorris?useUnicode=true&characterEncoding=UTF-8" 17 | user = "root" 18 | password = "password" 19 | maxPoolSize = 3 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /part6/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | ktor { 2 | deployment { 3 | port = 8080 4 | port = ${?PORT} 5 | } 6 | application { 7 | modules = [com.prof18.ktor.chucknorris.sample.ApplicationKt.module] 8 | } 9 | 10 | server { 11 | isProd = false 12 | } 13 | 14 | database { 15 | driverClass = "com.mysql.cj.jdbc.Driver" 16 | url = "jdbc:mysql://localhost:3308/chucknorris?useUnicode=true&characterEncoding=UTF-8" 17 | user = "root" 18 | password = "password" 19 | maxPoolSize = 3 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | ktor { 2 | deployment { 3 | port = 8080 4 | port = ${?PORT} 5 | } 6 | application { 7 | modules = [com.prof18.ktor.chucknorris.sample.ApplicationKt.module] 8 | } 9 | 10 | server { 11 | isProd = false 12 | } 13 | 14 | database { 15 | driverClass = "com.mysql.cj.jdbc.Driver" 16 | url = "jdbc:mysql://localhost:3308/chucknorris?useUnicode=true&characterEncoding=UTF-8" 17 | user = "root" 18 | password = "password" 19 | maxPoolSize = 3 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ktor-2.x/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | ktor { 2 | deployment { 3 | port = 8080 4 | port = ${?PORT} 5 | } 6 | application { 7 | modules = [com.prof18.ktor.chucknorris.sample.ApplicationKt.module] 8 | } 9 | 10 | server { 11 | isProd = false 12 | } 13 | 14 | database { 15 | driverClass = "com.mysql.cj.jdbc.Driver" 16 | url = "jdbc:mysql://localhost:3308/chucknorris?useUnicode=true&characterEncoding=UTF-8" 17 | user = "root" 18 | password = "password" 19 | maxPoolSize = 3 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /part2/README.md: -------------------------------------------------------------------------------- 1 | # Ktor Chuck Norris Sample 2 | 3 | A [Ktor](http://ktor.io) (version 1.6.x) sample project that returns Random Chuck Norris jokes. 4 | 5 | This branch contains all the code used in the [How to persist Ktor logs](https://www.marcogomiero.com/posts/2021/ktor-logging-on-disk/) article. For a summary of all the articles, go to the [ktor-1.6.x folder] 6 | (https://github.com/prof18/ktor-chuck-norris-sample/tree/main/ktor-1.6.x) 7 | 8 | The data used for the databases are from the [Chuck Norris IO](https://github.com/chucknorris-io/chuck-db) project. 9 | -------------------------------------------------------------------------------- /part1/README.md: -------------------------------------------------------------------------------- 1 | # Ktor Chuck Norris Sample 2 | 3 | A [Ktor](http://ktor.io) (version 1.6.x) sample project that returns Random Chuck Norris jokes. 4 | 5 | This project contains all the code used in the [Structuring a Ktor project](https://www.marcogomiero.com/posts/2021/ktor-project-structure/) article. For a summary of all the articles, go to the [ktor-1.6.x folder] 6 | (https://github.com/prof18/ktor-chuck-norris-sample/tree/main/ktor-1.6.x) 7 | 8 | The data used for the databases are from the [Chuck Norris IO](https://github.com/chucknorris-io/chuck-db) project. 9 | -------------------------------------------------------------------------------- /part1/src/main/kotlin/com/prof18/ktor/chucknorris/sample/di/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.di 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepositoryImpl 6 | import org.koin.dsl.module 7 | import org.koin.dsl.single 8 | 9 | val appModule = module { 10 | // Backend Config 11 | single() 12 | single { JokeRepositoryImpl() } 13 | } 14 | -------------------------------------------------------------------------------- /part2/src/main/kotlin/com/prof18/ktor/chucknorris/sample/di/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.di 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepositoryImpl 6 | import org.koin.dsl.module 7 | import org.koin.dsl.single 8 | 9 | val appModule = module { 10 | // Backend Config 11 | single() 12 | single { JokeRepositoryImpl() } 13 | } 14 | -------------------------------------------------------------------------------- /part6/README.md: -------------------------------------------------------------------------------- 1 | # Ktor Chuck Norris Sample 2 | 3 | A [Ktor](http://ktor.io) (version 1.6.x) sample project that returns Random Chuck Norris jokes. 4 | 5 | This branch contains all the code used in the [How to schedule jobs with Quartz on Ktor](https://www.marcogomiero.com/posts/2022/ktor-jobs-quartz/) article. For a summary of all the articles, go to the [ktor-1.6.x folder] 6 | (https://github.com/prof18/ktor-chuck-norris-sample/tree/main/ktor-1.6.x) 7 | 8 | The data used for the databases are from the [Chuck Norris IO](https://github.com/chucknorris-io/chuck-db) project. 9 | -------------------------------------------------------------------------------- /part1/src/test/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/fake/JokeRepositoryFake.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.fake 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 5 | 6 | class JokeRepositoryFake: JokeRepository { 7 | 8 | override suspend fun getRandomJoke(): JokeDTO { 9 | return JokeDTO( 10 | jokeId = "fake-id", 11 | jokeContent = "fake-content" 12 | ) 13 | } 14 | } -------------------------------------------------------------------------------- /part2/src/test/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/fake/JokeRepositoryFake.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.fake 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 5 | 6 | class JokeRepositoryFake: JokeRepository { 7 | 8 | override suspend fun getRandomJoke(): JokeDTO { 9 | return JokeDTO( 10 | jokeId = "fake-id", 11 | jokeContent = "fake-content" 12 | ) 13 | } 14 | } -------------------------------------------------------------------------------- /part5/README.md: -------------------------------------------------------------------------------- 1 | # Ktor Chuck Norris Sample 2 | 3 | A [Ktor](http://ktor.io) (version 1.6.x) sample project that returns Random Chuck Norris jokes. 4 | 5 | This branch contains all the code used in the [Generate API documentation from Swagger on Ktor](https://www.marcogomiero.com/posts/2022/ktor-setup-documentation/) article. For a summary of all the articles, go to the [ktor-1.6.x folder] 6 | (https://github.com/prof18/ktor-chuck-norris-sample/tree/main/ktor-1.6.x) 7 | 8 | The data used for the databases are from the [Chuck Norris IO](https://github.com/chucknorris-io/chuck-db) project. 9 | -------------------------------------------------------------------------------- /part3/README.md: -------------------------------------------------------------------------------- 1 | # Ktor Chuck Norris Sample 2 | 3 | A [Ktor](http://ktor.io) (version 1.6.x) sample project that returns Random Chuck Norris jokes. 4 | 5 | This branch contains all the code used in the [How to use an in-memory database for testing on Ktor](https://www.marcogomiero.com/posts/2021/ktor-in-memory-db-testing/) article. For a summary of all the articles, go to the [ktor-1.6.x folder] 6 | (https://github.com/prof18/ktor-chuck-norris-sample/tree/main/ktor-1.6.x) 7 | 8 | The data used for the databases are from the [Chuck Norris IO](https://github.com/chucknorris-io/chuck-db) project. 9 | -------------------------------------------------------------------------------- /part3/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/JokeLocalDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.JokeTable 5 | import org.jetbrains.exposed.sql.selectAll 6 | 7 | class JokeLocalDataSourceImpl : JokeLocalDataSource { 8 | 9 | override fun getAllJokes(): List { 10 | val query = JokeTable.selectAll() 11 | return Joke.wrapRows(query).toList() 12 | } 13 | } -------------------------------------------------------------------------------- /part4/README.md: -------------------------------------------------------------------------------- 1 | # Ktor Chuck Norris Sample 2 | 3 | A [Ktor](http://ktor.io) (version 1.6.x) sample project that returns Random Chuck Norris jokes. 4 | 5 | This branch contains all the code used in the [How to handle database migrations with Liquibase on Ktor](https://www.marcogomiero.com/posts/2022/ktor-migration-liquibase/) article. For a summary of all the articles, go to the [ktor-1.6.x folder] 6 | (https://github.com/prof18/ktor-chuck-norris-sample/tree/main/ktor-1.6.x) 7 | 8 | The data used for the databases are from the [Chuck Norris IO](https://github.com/chucknorris-io/chuck-db) project. 9 | -------------------------------------------------------------------------------- /part4/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/JokeLocalDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.JokeTable 5 | import org.jetbrains.exposed.sql.selectAll 6 | 7 | class JokeLocalDataSourceImpl : JokeLocalDataSource { 8 | 9 | override fun getAllJokes(): List { 10 | val query = JokeTable.selectAll() 11 | return Joke.wrapRows(query).toList() 12 | } 13 | } -------------------------------------------------------------------------------- /part5/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/JokeLocalDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.JokeTable 5 | import org.jetbrains.exposed.sql.selectAll 6 | 7 | class JokeLocalDataSourceImpl : JokeLocalDataSource { 8 | 9 | override fun getAllJokes(): List { 10 | val query = JokeTable.selectAll() 11 | return Joke.wrapRows(query).toList() 12 | } 13 | } -------------------------------------------------------------------------------- /part6/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/JokeLocalDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.JokeTable 5 | import org.jetbrains.exposed.sql.selectAll 6 | 7 | class JokeLocalDataSourceImpl : JokeLocalDataSource { 8 | 9 | override fun getAllJokes(): List { 10 | val query = JokeTable.selectAll() 11 | return Joke.wrapRows(query).toList() 12 | } 13 | } -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/JokeLocalDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.JokeTable 5 | import org.jetbrains.exposed.sql.selectAll 6 | 7 | class JokeLocalDataSourceImpl : JokeLocalDataSource { 8 | 9 | override fun getAllJokes(): List { 10 | val query = JokeTable.selectAll() 11 | return Joke.wrapRows(query).toList() 12 | } 13 | } -------------------------------------------------------------------------------- /ktor-2.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/JokeLocalDataSourceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.JokeTable 5 | import org.jetbrains.exposed.sql.selectAll 6 | 7 | class JokeLocalDataSourceImpl : JokeLocalDataSource { 8 | 9 | override fun getAllJokes(): List { 10 | val query = JokeTable.selectAll() 11 | return Joke.wrapRows(query).toList() 12 | } 13 | } -------------------------------------------------------------------------------- /part2/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ktor-2.x/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /part3/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /part4/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /part5/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /part6/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /ktor-1.6.x/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /ktor-2.x/src/main/resources/doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Api Doc 6 | 7 | 8 | 9 | 10 | 11 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /part5/src/main/resources/doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Api Doc 6 | 7 | 8 | 9 | 10 | 11 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /part6/src/main/resources/doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Api Doc 6 | 7 | 8 | 9 | 10 | 11 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/resources/doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Api Doc 6 | 7 | 8 | 9 | 10 | 11 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /part1/src/main/kotlin/com/prof18/ktor/chucknorris/sample/config/AppConfig.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.config 2 | 3 | import io.ktor.application.* 4 | import org.koin.ktor.ext.inject 5 | 6 | class AppConfig { 7 | lateinit var serverConfig: ServerConfig 8 | // Place here other configurations 9 | } 10 | 11 | fun Application.setupConfig() { 12 | val appConfig by inject() 13 | 14 | // Server 15 | val serverObject = environment.config.config("ktor.server") 16 | val isProd = serverObject.property("isProd").getString().toBoolean() 17 | appConfig.serverConfig = ServerConfig(isProd) 18 | 19 | } 20 | 21 | data class ServerConfig( 22 | val isProd: Boolean 23 | ) 24 | -------------------------------------------------------------------------------- /part2/src/main/kotlin/com/prof18/ktor/chucknorris/sample/config/AppConfig.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.config 2 | 3 | import io.ktor.application.* 4 | import org.koin.ktor.ext.inject 5 | 6 | class AppConfig { 7 | lateinit var serverConfig: ServerConfig 8 | // Place here other configurations 9 | } 10 | 11 | fun Application.setupConfig() { 12 | val appConfig by inject() 13 | 14 | // Server 15 | val serverObject = environment.config.config("ktor.server") 16 | val isProd = serverObject.property("isProd").getString().toBoolean() 17 | appConfig.serverConfig = ServerConfig(isProd) 18 | 19 | } 20 | 21 | data class ServerConfig( 22 | val isProd: Boolean 23 | ) 24 | -------------------------------------------------------------------------------- /ktor-2.x/src/main/resources/swagger/swagger.yml: -------------------------------------------------------------------------------- 1 | swagger: "2.0" 2 | info: 3 | title: Ktor Chuck Norris Sample 4 | description: A ktor sample project that returns Random Chuck Norris jokes 5 | version: 0.0.1 6 | tags: 7 | - name: Jokes 8 | description: Jokes Apis 9 | 10 | paths: 11 | /joke/random: 12 | get: 13 | summary: Get a random Chuck Norris Joke 14 | responses: 15 | "200": 16 | description: "JokeDTO" 17 | schema: 18 | $ref: "#/definitions/JokeDTO" 19 | 20 | definitions: 21 | JokeDTO: 22 | type: object 23 | properties: 24 | jokeId: 25 | type: string 26 | jokeContent: 27 | type: string 28 | required: 29 | - jokeId 30 | - jokeContent 31 | -------------------------------------------------------------------------------- /part5/src/main/resources/swagger/swagger.yml: -------------------------------------------------------------------------------- 1 | swagger: "2.0" 2 | info: 3 | title: Ktor Chuck Norris Sample 4 | description: A ktor sample project that returns Random Chuck Norris jokes 5 | version: 0.0.1 6 | tags: 7 | - name: Jokes 8 | description: Jokes Apis 9 | 10 | paths: 11 | /joke/random: 12 | get: 13 | summary: Get a random Chuck Norris Joke 14 | responses: 15 | "200": 16 | description: "JokeDTO" 17 | schema: 18 | $ref: "#/definitions/JokeDTO" 19 | 20 | definitions: 21 | JokeDTO: 22 | type: object 23 | properties: 24 | jokeId: 25 | type: string 26 | jokeContent: 27 | type: string 28 | required: 29 | - jokeId 30 | - jokeContent 31 | -------------------------------------------------------------------------------- /part6/src/main/resources/swagger/swagger.yml: -------------------------------------------------------------------------------- 1 | swagger: "2.0" 2 | info: 3 | title: Ktor Chuck Norris Sample 4 | description: A ktor sample project that returns Random Chuck Norris jokes 5 | version: 0.0.1 6 | tags: 7 | - name: Jokes 8 | description: Jokes Apis 9 | 10 | paths: 11 | /joke/random: 12 | get: 13 | summary: Get a random Chuck Norris Joke 14 | responses: 15 | "200": 16 | description: "JokeDTO" 17 | schema: 18 | $ref: "#/definitions/JokeDTO" 19 | 20 | definitions: 21 | JokeDTO: 22 | type: object 23 | properties: 24 | jokeId: 25 | type: string 26 | jokeContent: 27 | type: string 28 | required: 29 | - jokeId 30 | - jokeContent 31 | -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/resources/swagger/swagger.yml: -------------------------------------------------------------------------------- 1 | swagger: "2.0" 2 | info: 3 | title: Ktor Chuck Norris Sample 4 | description: A ktor sample project that returns Random Chuck Norris jokes 5 | version: 0.0.1 6 | tags: 7 | - name: Jokes 8 | description: Jokes Apis 9 | 10 | paths: 11 | /joke/random: 12 | get: 13 | summary: Get a random Chuck Norris Joke 14 | responses: 15 | "200": 16 | description: "JokeDTO" 17 | schema: 18 | $ref: "#/definitions/JokeDTO" 19 | 20 | definitions: 21 | JokeDTO: 22 | type: object 23 | properties: 24 | jokeId: 25 | type: string 26 | jokeContent: 27 | type: string 28 | required: 29 | - jokeId 30 | - jokeContent 31 | -------------------------------------------------------------------------------- /part5/src/main/resources/doc/swagger.yml: -------------------------------------------------------------------------------- 1 | swagger: "2.0" 2 | info: 3 | title: Ktor Chuck Norris Sample 4 | description: A ktor sample project that returns Random Chuck Norris jokes 5 | version: 0.0.1 6 | tags: 7 | - name: Jokes 8 | description: Jokes Apis 9 | 10 | paths: 11 | /joke/random: 12 | get: 13 | summary: Get a random Chuck Norris Joke 14 | description: Optional extended description in CommonMark or HTML. 15 | responses: 16 | "200": 17 | description: "JokeDTO" 18 | schema: 19 | $ref: "#/definitions/JokeDTO" 20 | 21 | definitions: 22 | JokeDTO: 23 | type: object 24 | properties: 25 | jokeId: 26 | type: string 27 | jokeContent: 28 | type: string 29 | required: 30 | - jokeId 31 | - jokeContent 32 | -------------------------------------------------------------------------------- /part6/src/main/resources/doc/swagger.yml: -------------------------------------------------------------------------------- 1 | swagger: "2.0" 2 | info: 3 | title: Ktor Chuck Norris Sample 4 | description: A ktor sample project that returns Random Chuck Norris jokes 5 | version: 0.0.1 6 | tags: 7 | - name: Jokes 8 | description: Jokes Apis 9 | 10 | paths: 11 | /joke/random: 12 | get: 13 | summary: Get a random Chuck Norris Joke 14 | description: Optional extended description in CommonMark or HTML. 15 | responses: 16 | "200": 17 | description: "JokeDTO" 18 | schema: 19 | $ref: "#/definitions/JokeDTO" 20 | 21 | definitions: 22 | JokeDTO: 23 | type: object 24 | properties: 25 | jokeId: 26 | type: string 27 | jokeContent: 28 | type: string 29 | required: 30 | - jokeId 31 | - jokeContent 32 | -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/resources/doc/swagger.yml: -------------------------------------------------------------------------------- 1 | swagger: "2.0" 2 | info: 3 | title: Ktor Chuck Norris Sample 4 | description: A ktor sample project that returns Random Chuck Norris jokes 5 | version: 0.0.1 6 | tags: 7 | - name: Jokes 8 | description: Jokes Apis 9 | 10 | paths: 11 | /joke/random: 12 | get: 13 | summary: Get a random Chuck Norris Joke 14 | description: Optional extended description in CommonMark or HTML. 15 | responses: 16 | "200": 17 | description: "JokeDTO" 18 | schema: 19 | $ref: "#/definitions/JokeDTO" 20 | 21 | definitions: 22 | JokeDTO: 23 | type: object 24 | properties: 25 | jokeId: 26 | type: string 27 | jokeContent: 28 | type: string 29 | required: 30 | - jokeId 31 | - jokeContent 32 | -------------------------------------------------------------------------------- /ktor-2.x/src/main/resources/doc/swagger.yml: -------------------------------------------------------------------------------- 1 | swagger: "2.0" 2 | info: 3 | title: Ktor Chuck Norris Sample 4 | description: A ktor sample project that returns Random Chuck Norris jokes 5 | version: 0.0.1 6 | tags: 7 | - name: Jokes 8 | description: Jokes Apis 9 | 10 | paths: 11 | /joke/random: 12 | get: 13 | summary: Get a random Chuck Norris Joke 14 | description: Optional extended description in CommonMark or HTML. 15 | responses: 16 | "200": 17 | description: "JokeDTO" 18 | schema: 19 | $ref: "#/definitions/JokeDTO" 20 | 21 | definitions: 22 | JokeDTO: 23 | type: object 24 | properties: 25 | jokeId: 26 | type: string 27 | jokeContent: 28 | type: string 29 | required: 30 | - jokeId 31 | - jokeContent 32 | -------------------------------------------------------------------------------- /part1/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/resource/JokeResource.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.resource 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 4 | import io.ktor.application.* 5 | import io.ktor.locations.* 6 | import io.ktor.response.* 7 | import io.ktor.routing.* 8 | import org.koin.ktor.ext.inject 9 | 10 | @KtorExperimentalLocationsAPI 11 | @Location("joke") 12 | class JokeEndpoint { 13 | 14 | @Location("/random") 15 | class Random(val parent: JokeEndpoint) 16 | } 17 | 18 | @KtorExperimentalLocationsAPI 19 | fun Route.jokeEndpoint() { 20 | 21 | val jokeRepository by inject() 22 | 23 | get { 24 | call.respond(jokeRepository.getRandomJoke()) 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /part2/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/resource/JokeResource.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.resource 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 4 | import io.ktor.application.* 5 | import io.ktor.locations.* 6 | import io.ktor.response.* 7 | import io.ktor.routing.* 8 | import org.koin.ktor.ext.inject 9 | 10 | @KtorExperimentalLocationsAPI 11 | @Location("joke") 12 | class JokeEndpoint { 13 | 14 | @Location("/random") 15 | class Random(val parent: JokeEndpoint) 16 | } 17 | 18 | @KtorExperimentalLocationsAPI 19 | fun Route.jokeEndpoint() { 20 | 21 | val jokeRepository by inject() 22 | 23 | get { 24 | call.respond(jokeRepository.getRandomJoke()) 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /part3/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/resource/JokeResource.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.resource 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 4 | import io.ktor.application.* 5 | import io.ktor.locations.* 6 | import io.ktor.response.* 7 | import io.ktor.routing.* 8 | import org.koin.ktor.ext.inject 9 | 10 | @KtorExperimentalLocationsAPI 11 | @Location("joke") 12 | class JokeEndpoint { 13 | 14 | @Location("/random") 15 | class Random(val parent: JokeEndpoint) 16 | } 17 | 18 | @KtorExperimentalLocationsAPI 19 | fun Route.jokeEndpoint() { 20 | 21 | val jokeRepository by inject() 22 | 23 | get { 24 | call.respond(jokeRepository.getRandomJoke()) 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /part4/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/resource/JokeResource.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.resource 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 4 | import io.ktor.application.* 5 | import io.ktor.locations.* 6 | import io.ktor.response.* 7 | import io.ktor.routing.* 8 | import org.koin.ktor.ext.inject 9 | 10 | @KtorExperimentalLocationsAPI 11 | @Location("joke") 12 | class JokeEndpoint { 13 | 14 | @Location("/random") 15 | class Random(val parent: JokeEndpoint) 16 | } 17 | 18 | @KtorExperimentalLocationsAPI 19 | fun Route.jokeEndpoint() { 20 | 21 | val jokeRepository by inject() 22 | 23 | get { 24 | call.respond(jokeRepository.getRandomJoke()) 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /part5/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/resource/JokeResource.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.resource 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 4 | import io.ktor.application.* 5 | import io.ktor.locations.* 6 | import io.ktor.response.* 7 | import io.ktor.routing.* 8 | import org.koin.ktor.ext.inject 9 | 10 | @KtorExperimentalLocationsAPI 11 | @Location("joke") 12 | class JokeEndpoint { 13 | 14 | @Location("/random") 15 | class Random(val parent: JokeEndpoint) 16 | } 17 | 18 | @KtorExperimentalLocationsAPI 19 | fun Route.jokeEndpoint() { 20 | 21 | val jokeRepository by inject() 22 | 23 | get { 24 | call.respond(jokeRepository.getRandomJoke()) 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /part3/src/main/resources/db/migration/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | Jokes Table 9 | 10 | 11 | 12 | 13 | Jokes Data 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /part4/src/main/resources/db/migration/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | Jokes Table 9 | 10 | 11 | 12 | 13 | Jokes Data 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /part5/src/main/resources/db/migration/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | Jokes Table 9 | 10 | 11 | 12 | 13 | Jokes Data 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ktor-2.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/jobs/JobFactory.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.jobs 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 4 | import org.quartz.Job 5 | import org.quartz.Scheduler 6 | import org.quartz.spi.JobFactory 7 | import org.quartz.spi.TriggerFiredBundle 8 | import kotlin.reflect.jvm.jvmName 9 | 10 | class JobFactory( 11 | private val jokeRepository: JokeRepository 12 | ): JobFactory { 13 | 14 | override fun newJob(bundle: TriggerFiredBundle?, scheduler: Scheduler?): Job { 15 | if (bundle != null) { 16 | val jobClass = bundle.jobDetail.jobClass 17 | if (jobClass.name == RandomJokeJob::class.jvmName) { 18 | return RandomJokeJob(jokeRepository) 19 | } 20 | } 21 | throw NotImplementedError("Job Factory error") 22 | } 23 | } -------------------------------------------------------------------------------- /part6/src/main/kotlin/com/prof18/ktor/chucknorris/sample/jobs/JobFactory.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.jobs 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 4 | import org.quartz.Job 5 | import org.quartz.Scheduler 6 | import org.quartz.spi.JobFactory 7 | import org.quartz.spi.TriggerFiredBundle 8 | import kotlin.reflect.jvm.jvmName 9 | 10 | class JobFactory( 11 | private val jokeRepository: JokeRepository 12 | ): JobFactory { 13 | 14 | override fun newJob(bundle: TriggerFiredBundle?, scheduler: Scheduler?): Job { 15 | if (bundle != null) { 16 | val jobClass = bundle.jobDetail.jobClass 17 | if (jobClass.name == RandomJokeJob::class.jvmName) { 18 | return RandomJokeJob(jokeRepository) 19 | } 20 | } 21 | throw NotImplementedError("Job Factory error") 22 | } 23 | } -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/jobs/JobFactory.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.jobs 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 4 | import org.quartz.Job 5 | import org.quartz.Scheduler 6 | import org.quartz.spi.JobFactory 7 | import org.quartz.spi.TriggerFiredBundle 8 | import kotlin.reflect.jvm.jvmName 9 | 10 | class JobFactory( 11 | private val jokeRepository: JokeRepository 12 | ): JobFactory { 13 | 14 | override fun newJob(bundle: TriggerFiredBundle?, scheduler: Scheduler?): Job { 15 | if (bundle != null) { 16 | val jobClass = bundle.jobDetail.jobClass 17 | if (jobClass.name == RandomJokeJob::class.jvmName) { 18 | return RandomJokeJob(jokeRepository) 19 | } 20 | } 21 | throw NotImplementedError("Job Factory error") 22 | } 23 | } -------------------------------------------------------------------------------- /part3/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/JokeRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSource 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.mapper.toDTO 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 6 | import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction 7 | 8 | class JokeRepositoryImpl( 9 | private val jokeLocalDataSource: JokeLocalDataSource 10 | ) : JokeRepository { 11 | 12 | override suspend fun getRandomJoke(): JokeDTO { 13 | val jokeDTO = newSuspendedTransaction { 14 | val allJokes = jokeLocalDataSource.getAllJokes() 15 | val randomJoke = allJokes.random() 16 | return@newSuspendedTransaction randomJoke.toDTO() 17 | } 18 | return jokeDTO 19 | } 20 | } -------------------------------------------------------------------------------- /part4/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/JokeRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSource 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.mapper.toDTO 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 6 | import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction 7 | 8 | class JokeRepositoryImpl( 9 | private val jokeLocalDataSource: JokeLocalDataSource 10 | ) : JokeRepository { 11 | 12 | override suspend fun getRandomJoke(): JokeDTO { 13 | val jokeDTO = newSuspendedTransaction { 14 | val allJokes = jokeLocalDataSource.getAllJokes() 15 | val randomJoke = allJokes.random() 16 | return@newSuspendedTransaction randomJoke.toDTO() 17 | } 18 | return jokeDTO 19 | } 20 | } -------------------------------------------------------------------------------- /part5/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/JokeRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSource 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.mapper.toDTO 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 6 | import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction 7 | 8 | class JokeRepositoryImpl( 9 | private val jokeLocalDataSource: JokeLocalDataSource 10 | ) : JokeRepository { 11 | 12 | override suspend fun getRandomJoke(): JokeDTO { 13 | val jokeDTO = newSuspendedTransaction { 14 | val allJokes = jokeLocalDataSource.getAllJokes() 15 | val randomJoke = allJokes.random() 16 | return@newSuspendedTransaction randomJoke.toDTO() 17 | } 18 | return jokeDTO 19 | } 20 | } -------------------------------------------------------------------------------- /part5/src/main/kotlin/com/prof18/ktor/chucknorris/sample/di/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.di 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 5 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactoryImpl 6 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSource 7 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSourceImpl 8 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 9 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepositoryImpl 10 | import org.koin.dsl.module 11 | import org.koin.dsl.single 12 | 13 | val appModule = module { 14 | // Backend Config 15 | single() 16 | single { DatabaseFactoryImpl(get()) } 17 | single { JokeLocalDataSourceImpl() } 18 | single { JokeRepositoryImpl(get()) } 19 | } 20 | -------------------------------------------------------------------------------- /part3/src/main/kotlin/com/prof18/ktor/chucknorris/sample/di/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.di 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 5 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactoryImpl 6 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSource 7 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSourceImpl 8 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 9 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepositoryImpl 10 | import org.koin.dsl.module 11 | import org.koin.dsl.single 12 | 13 | val appModule = module { 14 | // Backend Config 15 | single() 16 | single { DatabaseFactoryImpl(get()) } 17 | single { JokeLocalDataSourceImpl() } 18 | single { JokeRepositoryImpl(get()) } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /part4/src/main/kotlin/com/prof18/ktor/chucknorris/sample/di/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.di 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 5 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactoryImpl 6 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSource 7 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSourceImpl 8 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 9 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepositoryImpl 10 | import org.koin.dsl.module 11 | import org.koin.dsl.single 12 | 13 | val appModule = module { 14 | // Backend Config 15 | single() 16 | single { DatabaseFactoryImpl(get()) } 17 | single { JokeLocalDataSourceImpl() } 18 | single { JokeRepositoryImpl(get()) } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /part3/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/dao/Joke.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data.dao 2 | 3 | import org.jetbrains.exposed.dao.Entity 4 | import org.jetbrains.exposed.dao.EntityClass 5 | import org.jetbrains.exposed.dao.id.EntityID 6 | import org.jetbrains.exposed.dao.id.IdTable 7 | import org.jetbrains.exposed.sql.Column 8 | import org.jetbrains.exposed.sql.javatime.datetime 9 | 10 | object JokeTable: IdTable(name = "joke") { 11 | val createdAt = datetime("created_at") 12 | val updatedAt = datetime("updated_at") 13 | val value = text("value") 14 | 15 | override val id: Column> = varchar("joke_id", 255).entityId() 16 | override val primaryKey: PrimaryKey = PrimaryKey(id) 17 | 18 | } 19 | 20 | class Joke(id: EntityID): Entity(id) { 21 | companion object: EntityClass(JokeTable) 22 | 23 | var createdAt by JokeTable.createdAt 24 | var updatedAt by JokeTable.updatedAt 25 | var value by JokeTable.value 26 | } -------------------------------------------------------------------------------- /part4/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/dao/Joke.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data.dao 2 | 3 | import org.jetbrains.exposed.dao.Entity 4 | import org.jetbrains.exposed.dao.EntityClass 5 | import org.jetbrains.exposed.dao.id.EntityID 6 | import org.jetbrains.exposed.dao.id.IdTable 7 | import org.jetbrains.exposed.sql.Column 8 | import org.jetbrains.exposed.sql.javatime.datetime 9 | 10 | object JokeTable: IdTable(name = "joke") { 11 | val createdAt = datetime("created_at") 12 | val updatedAt = datetime("updated_at") 13 | val value = text("value") 14 | 15 | override val id: Column> = varchar("joke_id", 255).entityId() 16 | override val primaryKey: PrimaryKey = PrimaryKey(id) 17 | 18 | } 19 | 20 | class Joke(id: EntityID): Entity(id) { 21 | companion object: EntityClass(JokeTable) 22 | 23 | var createdAt by JokeTable.createdAt 24 | var updatedAt by JokeTable.updatedAt 25 | var value by JokeTable.value 26 | } -------------------------------------------------------------------------------- /part5/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/dao/Joke.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data.dao 2 | 3 | import org.jetbrains.exposed.dao.Entity 4 | import org.jetbrains.exposed.dao.EntityClass 5 | import org.jetbrains.exposed.dao.id.EntityID 6 | import org.jetbrains.exposed.dao.id.IdTable 7 | import org.jetbrains.exposed.sql.Column 8 | import org.jetbrains.exposed.sql.javatime.datetime 9 | 10 | object JokeTable: IdTable(name = "joke") { 11 | val createdAt = datetime("created_at") 12 | val updatedAt = datetime("updated_at") 13 | val value = text("value") 14 | 15 | override val id: Column> = varchar("joke_id", 255).entityId() 16 | override val primaryKey: PrimaryKey = PrimaryKey(id) 17 | 18 | } 19 | 20 | class Joke(id: EntityID): Entity(id) { 21 | companion object: EntityClass(JokeTable) 22 | 23 | var createdAt by JokeTable.createdAt 24 | var updatedAt by JokeTable.updatedAt 25 | var value by JokeTable.value 26 | } -------------------------------------------------------------------------------- /part6/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/dao/Joke.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data.dao 2 | 3 | import org.jetbrains.exposed.dao.Entity 4 | import org.jetbrains.exposed.dao.EntityClass 5 | import org.jetbrains.exposed.dao.id.EntityID 6 | import org.jetbrains.exposed.dao.id.IdTable 7 | import org.jetbrains.exposed.sql.Column 8 | import org.jetbrains.exposed.sql.javatime.datetime 9 | 10 | object JokeTable: IdTable(name = "joke") { 11 | val createdAt = datetime("created_at") 12 | val updatedAt = datetime("updated_at") 13 | val value = text("value") 14 | 15 | override val id: Column> = varchar("joke_id", 255).entityId() 16 | override val primaryKey: PrimaryKey = PrimaryKey(id) 17 | 18 | } 19 | 20 | class Joke(id: EntityID): Entity(id) { 21 | companion object: EntityClass(JokeTable) 22 | 23 | var createdAt by JokeTable.createdAt 24 | var updatedAt by JokeTable.updatedAt 25 | var value by JokeTable.value 26 | } -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/dao/Joke.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data.dao 2 | 3 | import org.jetbrains.exposed.dao.Entity 4 | import org.jetbrains.exposed.dao.EntityClass 5 | import org.jetbrains.exposed.dao.id.EntityID 6 | import org.jetbrains.exposed.dao.id.IdTable 7 | import org.jetbrains.exposed.sql.Column 8 | import org.jetbrains.exposed.sql.javatime.datetime 9 | 10 | object JokeTable: IdTable(name = "joke") { 11 | val createdAt = datetime("created_at") 12 | val updatedAt = datetime("updated_at") 13 | val value = text("value") 14 | 15 | override val id: Column> = varchar("joke_id", 255).entityId() 16 | override val primaryKey: PrimaryKey = PrimaryKey(id) 17 | 18 | } 19 | 20 | class Joke(id: EntityID): Entity(id) { 21 | companion object: EntityClass(JokeTable) 22 | 23 | var createdAt by JokeTable.createdAt 24 | var updatedAt by JokeTable.updatedAt 25 | var value by JokeTable.value 26 | } -------------------------------------------------------------------------------- /ktor-2.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/data/dao/Joke.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.data.dao 2 | 3 | import org.jetbrains.exposed.dao.Entity 4 | import org.jetbrains.exposed.dao.EntityClass 5 | import org.jetbrains.exposed.dao.id.EntityID 6 | import org.jetbrains.exposed.dao.id.IdTable 7 | import org.jetbrains.exposed.sql.Column 8 | import org.jetbrains.exposed.sql.javatime.datetime 9 | 10 | object JokeTable: IdTable(name = "joke") { 11 | val createdAt = datetime("created_at") 12 | val updatedAt = datetime("updated_at") 13 | val value = text("value") 14 | 15 | override val id: Column> = varchar("joke_id", 255).entityId() 16 | override val primaryKey: PrimaryKey = PrimaryKey(id) 17 | 18 | } 19 | 20 | class Joke(id: EntityID): Entity(id) { 21 | companion object: EntityClass(JokeTable) 22 | 23 | var createdAt by JokeTable.createdAt 24 | var updatedAt by JokeTable.updatedAt 25 | var value by JokeTable.value 26 | } -------------------------------------------------------------------------------- /part6/src/main/resources/db/migration/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | Jokes Table 9 | 10 | 11 | 12 | 13 | Jokes Data 14 | 15 | 16 | 17 | 18 | Tables for Quartz 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/resources/db/migration/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | Jokes Table 9 | 10 | 11 | 12 | 13 | Jokes Data 14 | 15 | 16 | 17 | 18 | Tables for Quartz 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ktor-2.x/src/main/resources/db/migration/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | Jokes Table 9 | 10 | 11 | 12 | 13 | Jokes Data 14 | 15 | 16 | 17 | 18 | Tables for Quartz 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ktor-2.x/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/database/DatabaseFactoryForUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 4 | import com.zaxxer.hikari.HikariConfig 5 | import com.zaxxer.hikari.HikariDataSource 6 | import org.jetbrains.exposed.sql.Database 7 | 8 | class DatabaseFactoryForUnitTest: DatabaseFactory { 9 | 10 | lateinit var source: HikariDataSource 11 | 12 | override fun close() { 13 | source.close() 14 | } 15 | 16 | override fun connect() { 17 | Database.connect(hikari()) 18 | SchemaDefinition.createSchema() 19 | } 20 | 21 | private fun hikari(): HikariDataSource { 22 | val config = HikariConfig() 23 | config.driverClassName = "org.h2.Driver" 24 | config.jdbcUrl = "jdbc:h2:mem:;DATABASE_TO_UPPER=false;MODE=MYSQL" 25 | config.maximumPoolSize = 2 26 | config.isAutoCommit = true 27 | config.validate() 28 | source = HikariDataSource(config) 29 | return source 30 | } 31 | } -------------------------------------------------------------------------------- /part1/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/TestServer.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.module 5 | import io.ktor.config.* 6 | import io.ktor.locations.* 7 | import io.ktor.server.testing.* 8 | import org.koin.core.module.Module 9 | import org.koin.dsl.module 10 | import org.koin.dsl.single 11 | 12 | fun MapApplicationConfig.createConfigForTesting() { 13 | // Server config 14 | put("ktor.server.isProd", "false") 15 | } 16 | 17 | 18 | @KtorExperimentalLocationsAPI 19 | fun withTestServer(koinModules: List = listOf(appTestModule), block: TestApplicationEngine.() -> Unit) { 20 | withTestApplication( 21 | { 22 | (environment.config as MapApplicationConfig).apply { 23 | createConfigForTesting() 24 | } 25 | module(testing = true, koinModules = koinModules) 26 | }, block 27 | ) 28 | } 29 | 30 | val appTestModule = module { 31 | single() 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /part2/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/TestServer.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.module 5 | import io.ktor.config.* 6 | import io.ktor.locations.* 7 | import io.ktor.server.testing.* 8 | import org.koin.core.module.Module 9 | import org.koin.dsl.module 10 | import org.koin.dsl.single 11 | 12 | fun MapApplicationConfig.createConfigForTesting() { 13 | // Server config 14 | put("ktor.server.isProd", "false") 15 | } 16 | 17 | 18 | @KtorExperimentalLocationsAPI 19 | fun withTestServer(koinModules: List = listOf(appTestModule), block: TestApplicationEngine.() -> Unit) { 20 | withTestApplication( 21 | { 22 | (environment.config as MapApplicationConfig).apply { 23 | createConfigForTesting() 24 | } 25 | module(testing = true, koinModules = koinModules) 26 | }, block 27 | ) 28 | } 29 | 30 | val appTestModule = module { 31 | single() 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /part3/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/database/DatabaseFactoryForUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 4 | import com.zaxxer.hikari.HikariConfig 5 | import com.zaxxer.hikari.HikariDataSource 6 | import org.jetbrains.exposed.sql.Database 7 | 8 | class DatabaseFactoryForUnitTest: DatabaseFactory { 9 | 10 | lateinit var source: HikariDataSource 11 | 12 | override fun close() { 13 | source.close() 14 | } 15 | 16 | override fun connect() { 17 | Database.connect(hikari()) 18 | SchemaDefinition.createSchema() 19 | } 20 | 21 | private fun hikari(): HikariDataSource { 22 | val config = HikariConfig() 23 | config.driverClassName = "org.h2.Driver" 24 | config.jdbcUrl = "jdbc:h2:mem:;DATABASE_TO_UPPER=false;MODE=MYSQL" 25 | config.maximumPoolSize = 2 26 | config.isAutoCommit = true 27 | config.validate() 28 | source = HikariDataSource(config) 29 | return source 30 | } 31 | } -------------------------------------------------------------------------------- /part4/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/database/DatabaseFactoryForUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 4 | import com.zaxxer.hikari.HikariConfig 5 | import com.zaxxer.hikari.HikariDataSource 6 | import org.jetbrains.exposed.sql.Database 7 | 8 | class DatabaseFactoryForUnitTest: DatabaseFactory { 9 | 10 | lateinit var source: HikariDataSource 11 | 12 | override fun close() { 13 | source.close() 14 | } 15 | 16 | override fun connect() { 17 | Database.connect(hikari()) 18 | SchemaDefinition.createSchema() 19 | } 20 | 21 | private fun hikari(): HikariDataSource { 22 | val config = HikariConfig() 23 | config.driverClassName = "org.h2.Driver" 24 | config.jdbcUrl = "jdbc:h2:mem:;DATABASE_TO_UPPER=false;MODE=MYSQL" 25 | config.maximumPoolSize = 2 26 | config.isAutoCommit = true 27 | config.validate() 28 | source = HikariDataSource(config) 29 | return source 30 | } 31 | } -------------------------------------------------------------------------------- /part5/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/database/DatabaseFactoryForUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 4 | import com.zaxxer.hikari.HikariConfig 5 | import com.zaxxer.hikari.HikariDataSource 6 | import org.jetbrains.exposed.sql.Database 7 | 8 | class DatabaseFactoryForUnitTest: DatabaseFactory { 9 | 10 | lateinit var source: HikariDataSource 11 | 12 | override fun close() { 13 | source.close() 14 | } 15 | 16 | override fun connect() { 17 | Database.connect(hikari()) 18 | SchemaDefinition.createSchema() 19 | } 20 | 21 | private fun hikari(): HikariDataSource { 22 | val config = HikariConfig() 23 | config.driverClassName = "org.h2.Driver" 24 | config.jdbcUrl = "jdbc:h2:mem:;DATABASE_TO_UPPER=false;MODE=MYSQL" 25 | config.maximumPoolSize = 2 26 | config.isAutoCommit = true 27 | config.validate() 28 | source = HikariDataSource(config) 29 | return source 30 | } 31 | } -------------------------------------------------------------------------------- /part6/src/main/kotlin/com/prof18/ktor/chucknorris/sample/jobs/RandomJokeJob.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.jobs 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 4 | import org.quartz.Job 5 | import org.quartz.JobExecutionContext 6 | 7 | class RandomJokeJob( 8 | private val jokeRepository: JokeRepository 9 | ) : Job { 10 | 11 | override fun execute(context: JobExecutionContext?) { 12 | if (context == null) { 13 | return 14 | } 15 | 16 | val dataMap = context.jobDetail.jobDataMap 17 | 18 | val name: String? = try { 19 | dataMap.getString(JOB_MAP_NAME_ID_KEY) 20 | } catch (e: ClassCastException) { 21 | null 22 | } 23 | 24 | if (name != null) { 25 | val greetingMessage = jokeRepository.getChuckGreeting(name) 26 | 27 | println(greetingMessage) 28 | } 29 | } 30 | 31 | companion object { 32 | const val JOB_MAP_NAME_ID_KEY = "name" 33 | const val WATCH_JOB_GROUP = "WatchJob" 34 | 35 | } 36 | } 37 | 38 | 39 | -------------------------------------------------------------------------------- /part6/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/database/DatabaseFactoryForUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 4 | import com.zaxxer.hikari.HikariConfig 5 | import com.zaxxer.hikari.HikariDataSource 6 | import org.jetbrains.exposed.sql.Database 7 | 8 | class DatabaseFactoryForUnitTest: DatabaseFactory { 9 | 10 | lateinit var source: HikariDataSource 11 | 12 | override fun close() { 13 | source.close() 14 | } 15 | 16 | override fun connect() { 17 | Database.connect(hikari()) 18 | SchemaDefinition.createSchema() 19 | } 20 | 21 | private fun hikari(): HikariDataSource { 22 | val config = HikariConfig() 23 | config.driverClassName = "org.h2.Driver" 24 | config.jdbcUrl = "jdbc:h2:mem:;DATABASE_TO_UPPER=false;MODE=MYSQL" 25 | config.maximumPoolSize = 2 26 | config.isAutoCommit = true 27 | config.validate() 28 | source = HikariDataSource(config) 29 | return source 30 | } 31 | } -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/jobs/RandomJokeJob.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.jobs 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 4 | import org.quartz.Job 5 | import org.quartz.JobExecutionContext 6 | 7 | class RandomJokeJob( 8 | private val jokeRepository: JokeRepository 9 | ) : Job { 10 | 11 | override fun execute(context: JobExecutionContext?) { 12 | if (context == null) { 13 | return 14 | } 15 | 16 | val dataMap = context.jobDetail.jobDataMap 17 | 18 | val name: String? = try { 19 | dataMap.getString(JOB_MAP_NAME_ID_KEY) 20 | } catch (e: ClassCastException) { 21 | null 22 | } 23 | 24 | if (name != null) { 25 | val greetingMessage = jokeRepository.getChuckGreeting(name) 26 | 27 | println(greetingMessage) 28 | } 29 | } 30 | 31 | companion object { 32 | const val JOB_MAP_NAME_ID_KEY = "name" 33 | const val WATCH_JOB_GROUP = "WatchJob" 34 | 35 | } 36 | } 37 | 38 | 39 | -------------------------------------------------------------------------------- /ktor-1.6.x/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/database/DatabaseFactoryForUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 4 | import com.zaxxer.hikari.HikariConfig 5 | import com.zaxxer.hikari.HikariDataSource 6 | import org.jetbrains.exposed.sql.Database 7 | 8 | class DatabaseFactoryForUnitTest: DatabaseFactory { 9 | 10 | lateinit var source: HikariDataSource 11 | 12 | override fun close() { 13 | source.close() 14 | } 15 | 16 | override fun connect() { 17 | Database.connect(hikari()) 18 | SchemaDefinition.createSchema() 19 | } 20 | 21 | private fun hikari(): HikariDataSource { 22 | val config = HikariConfig() 23 | config.driverClassName = "org.h2.Driver" 24 | config.jdbcUrl = "jdbc:h2:mem:;DATABASE_TO_UPPER=false;MODE=MYSQL" 25 | config.maximumPoolSize = 2 26 | config.isAutoCommit = true 27 | config.validate() 28 | source = HikariDataSource(config) 29 | return source 30 | } 31 | } -------------------------------------------------------------------------------- /ktor-2.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/jobs/RandomJokeJob.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.jobs 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 4 | import org.quartz.Job 5 | import org.quartz.JobExecutionContext 6 | 7 | class RandomJokeJob( 8 | private val jokeRepository: JokeRepository 9 | ) : Job { 10 | 11 | override fun execute(context: JobExecutionContext?) { 12 | if (context == null) { 13 | return 14 | } 15 | 16 | val dataMap = context.jobDetail.jobDataMap 17 | 18 | val name: String? = try { 19 | dataMap.getString(JOB_MAP_NAME_ID_KEY) 20 | } catch (e: ClassCastException) { 21 | null 22 | } 23 | 24 | if (name != null) { 25 | val greetingMessage = jokeRepository.getChuckGreeting(name) 26 | 27 | println(greetingMessage) 28 | } 29 | } 30 | 31 | companion object { 32 | const val JOB_MAP_NAME_ID_KEY = "name" 33 | const val WATCH_JOB_GROUP = "WatchJob" 34 | 35 | } 36 | } 37 | 38 | 39 | -------------------------------------------------------------------------------- /part6/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/resource/JokeResource.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.resource 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 4 | import io.ktor.application.* 5 | import io.ktor.locations.* 6 | import io.ktor.locations.post 7 | import io.ktor.response.* 8 | import io.ktor.routing.* 9 | import org.koin.ktor.ext.inject 10 | 11 | @KtorExperimentalLocationsAPI 12 | @Location("joke") 13 | class JokeEndpoint { 14 | 15 | @Location("/random") 16 | class Random(val parent: JokeEndpoint) 17 | 18 | @Location("/watch/{name}") 19 | class Watch(val name: String, val parent: JokeEndpoint) 20 | } 21 | 22 | @KtorExperimentalLocationsAPI 23 | fun Route.jokeEndpoint() { 24 | 25 | val jokeRepository by inject() 26 | 27 | get { 28 | call.respond(jokeRepository.getRandomJoke()) 29 | } 30 | 31 | post { apiCallParams -> 32 | val name = apiCallParams.name 33 | jokeRepository.watch(name) 34 | call.respond("Ok") 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/resource/JokeResource.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.resource 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 4 | import io.ktor.application.* 5 | import io.ktor.locations.* 6 | import io.ktor.locations.post 7 | import io.ktor.response.* 8 | import io.ktor.routing.* 9 | import org.koin.ktor.ext.inject 10 | 11 | @KtorExperimentalLocationsAPI 12 | @Location("joke") 13 | class JokeEndpoint { 14 | 15 | @Location("/random") 16 | class Random(val parent: JokeEndpoint) 17 | 18 | @Location("/watch/{name}") 19 | class Watch(val name: String, val parent: JokeEndpoint) 20 | } 21 | 22 | @KtorExperimentalLocationsAPI 23 | fun Route.jokeEndpoint() { 24 | 25 | val jokeRepository by inject() 26 | 27 | get { 28 | call.respond(jokeRepository.getRandomJoke()) 29 | } 30 | 31 | post { apiCallParams -> 32 | val name = apiCallParams.name 33 | jokeRepository.watch(name) 34 | call.respond("Ok") 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /part3/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/database/DatabaseFactoryForServerTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 5 | import com.zaxxer.hikari.HikariConfig 6 | import com.zaxxer.hikari.HikariDataSource 7 | import org.jetbrains.exposed.sql.Database 8 | 9 | class DatabaseFactoryForServerTest(appConfig: AppConfig): DatabaseFactory { 10 | 11 | private val dbConfig = appConfig.databaseConfig 12 | 13 | override fun connect() { 14 | Database.connect(hikari()) 15 | SchemaDefinition.createSchema() 16 | } 17 | 18 | override fun close() { 19 | // not needed 20 | } 21 | 22 | private fun hikari(): HikariDataSource { 23 | val config = HikariConfig() 24 | config.driverClassName = dbConfig.driverClass 25 | config.jdbcUrl = dbConfig.url 26 | config.maximumPoolSize = dbConfig.maxPoolSize 27 | config.isAutoCommit = true 28 | config.validate() 29 | return HikariDataSource(config) 30 | } 31 | } -------------------------------------------------------------------------------- /part4/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/database/DatabaseFactoryForServerTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 5 | import com.zaxxer.hikari.HikariConfig 6 | import com.zaxxer.hikari.HikariDataSource 7 | import org.jetbrains.exposed.sql.Database 8 | 9 | class DatabaseFactoryForServerTest(appConfig: AppConfig): DatabaseFactory { 10 | 11 | private val dbConfig = appConfig.databaseConfig 12 | 13 | override fun connect() { 14 | Database.connect(hikari()) 15 | SchemaDefinition.createSchema() 16 | } 17 | 18 | override fun close() { 19 | // not needed 20 | } 21 | 22 | private fun hikari(): HikariDataSource { 23 | val config = HikariConfig() 24 | config.driverClassName = dbConfig.driverClass 25 | config.jdbcUrl = dbConfig.url 26 | config.maximumPoolSize = dbConfig.maxPoolSize 27 | config.isAutoCommit = true 28 | config.validate() 29 | return HikariDataSource(config) 30 | } 31 | } -------------------------------------------------------------------------------- /part5/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/database/DatabaseFactoryForServerTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 5 | import com.zaxxer.hikari.HikariConfig 6 | import com.zaxxer.hikari.HikariDataSource 7 | import org.jetbrains.exposed.sql.Database 8 | 9 | class DatabaseFactoryForServerTest(appConfig: AppConfig): DatabaseFactory { 10 | 11 | private val dbConfig = appConfig.databaseConfig 12 | 13 | override fun connect() { 14 | Database.connect(hikari()) 15 | SchemaDefinition.createSchema() 16 | } 17 | 18 | override fun close() { 19 | // not needed 20 | } 21 | 22 | private fun hikari(): HikariDataSource { 23 | val config = HikariConfig() 24 | config.driverClassName = dbConfig.driverClass 25 | config.jdbcUrl = dbConfig.url 26 | config.maximumPoolSize = dbConfig.maxPoolSize 27 | config.isAutoCommit = true 28 | config.validate() 29 | return HikariDataSource(config) 30 | } 31 | } -------------------------------------------------------------------------------- /part6/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/database/DatabaseFactoryForServerTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 5 | import com.zaxxer.hikari.HikariConfig 6 | import com.zaxxer.hikari.HikariDataSource 7 | import org.jetbrains.exposed.sql.Database 8 | 9 | class DatabaseFactoryForServerTest(appConfig: AppConfig): DatabaseFactory { 10 | 11 | private val dbConfig = appConfig.databaseConfig 12 | 13 | override fun connect() { 14 | Database.connect(hikari()) 15 | SchemaDefinition.createSchema() 16 | } 17 | 18 | override fun close() { 19 | // not needed 20 | } 21 | 22 | private fun hikari(): HikariDataSource { 23 | val config = HikariConfig() 24 | config.driverClassName = dbConfig.driverClass 25 | config.jdbcUrl = dbConfig.url 26 | config.maximumPoolSize = dbConfig.maxPoolSize 27 | config.isAutoCommit = true 28 | config.validate() 29 | return HikariDataSource(config) 30 | } 31 | } -------------------------------------------------------------------------------- /ktor-1.6.x/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/database/DatabaseFactoryForServerTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 5 | import com.zaxxer.hikari.HikariConfig 6 | import com.zaxxer.hikari.HikariDataSource 7 | import org.jetbrains.exposed.sql.Database 8 | 9 | class DatabaseFactoryForServerTest(appConfig: AppConfig): DatabaseFactory { 10 | 11 | private val dbConfig = appConfig.databaseConfig 12 | 13 | override fun connect() { 14 | Database.connect(hikari()) 15 | SchemaDefinition.createSchema() 16 | } 17 | 18 | override fun close() { 19 | // not needed 20 | } 21 | 22 | private fun hikari(): HikariDataSource { 23 | val config = HikariConfig() 24 | config.driverClassName = dbConfig.driverClass 25 | config.jdbcUrl = dbConfig.url 26 | config.maximumPoolSize = dbConfig.maxPoolSize 27 | config.isAutoCommit = true 28 | config.validate() 29 | return HikariDataSource(config) 30 | } 31 | } -------------------------------------------------------------------------------- /ktor-2.x/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/database/DatabaseFactoryForServerTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 5 | import com.zaxxer.hikari.HikariConfig 6 | import com.zaxxer.hikari.HikariDataSource 7 | import org.jetbrains.exposed.sql.Database 8 | 9 | class DatabaseFactoryForServerTest(appConfig: AppConfig): DatabaseFactory { 10 | 11 | private val dbConfig = appConfig.databaseConfig 12 | 13 | override fun connect() { 14 | Database.connect(hikari()) 15 | SchemaDefinition.createSchema() 16 | } 17 | 18 | override fun close() { 19 | // not needed 20 | } 21 | 22 | private fun hikari(): HikariDataSource { 23 | val config = HikariConfig() 24 | config.driverClassName = dbConfig.driverClass 25 | config.jdbcUrl = dbConfig.url 26 | config.maximumPoolSize = dbConfig.maxPoolSize 27 | config.isAutoCommit = true 28 | config.validate() 29 | return HikariDataSource(config) 30 | } 31 | } -------------------------------------------------------------------------------- /ktor-2.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/resource/JokeResource.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.resource 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 4 | import io.ktor.resources.Resource 5 | import io.ktor.server.application.call 6 | import io.ktor.server.resources.get 7 | import io.ktor.server.resources.post 8 | import io.ktor.server.response.respond 9 | import io.ktor.server.routing.Route 10 | import org.koin.ktor.ext.inject 11 | 12 | @Resource("joke") 13 | class JokeEndpoint { 14 | 15 | @Resource("/random") 16 | class Random(val parent: JokeEndpoint = JokeEndpoint()) 17 | 18 | @Resource("/watch/{name}") 19 | class Watch(val name: String, val parent: JokeEndpoint = JokeEndpoint()) 20 | } 21 | 22 | fun Route.jokeEndpoint() { 23 | 24 | val jokeRepository by inject() 25 | 26 | get { 27 | call.respond(jokeRepository.getRandomJoke()) 28 | } 29 | 30 | post { apiCallParams -> 31 | val name = apiCallParams.name 32 | jokeRepository.watch(name) 33 | call.respond("Ok") 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /part6/src/main/kotlin/com/prof18/ktor/chucknorris/sample/di/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.di 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 5 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactoryImpl 6 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSource 7 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSourceImpl 8 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 9 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepositoryImpl 10 | import com.prof18.ktor.chucknorris.sample.jobs.JobFactory 11 | import com.prof18.ktor.chucknorris.sample.jobs.JobSchedulerManager 12 | import org.koin.dsl.module 13 | import org.koin.dsl.single 14 | 15 | val appModule = module { 16 | // Backend Config 17 | single() 18 | single { DatabaseFactoryImpl(get()) } 19 | single { JokeLocalDataSourceImpl() } 20 | single { JokeRepositoryImpl(get(), get()) } 21 | single() 22 | single() 23 | } 24 | -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/di/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.di 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 5 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactoryImpl 6 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSource 7 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSourceImpl 8 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 9 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepositoryImpl 10 | import com.prof18.ktor.chucknorris.sample.jobs.JobFactory 11 | import com.prof18.ktor.chucknorris.sample.jobs.JobSchedulerManager 12 | import org.koin.dsl.module 13 | import org.koin.dsl.single 14 | 15 | val appModule = module { 16 | // Backend Config 17 | single() 18 | single { DatabaseFactoryImpl(get()) } 19 | single { JokeLocalDataSourceImpl() } 20 | single { JokeRepositoryImpl(get(), get()) } 21 | single() 22 | single() 23 | } 24 | -------------------------------------------------------------------------------- /ktor-2.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/di/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.di 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 5 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactoryImpl 6 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSource 7 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSourceImpl 8 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 9 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepositoryImpl 10 | import com.prof18.ktor.chucknorris.sample.jobs.JobFactory 11 | import com.prof18.ktor.chucknorris.sample.jobs.JobSchedulerManager 12 | import org.koin.core.module.dsl.singleOf 13 | import org.koin.dsl.module 14 | 15 | val appModule = module { 16 | // Backend Config 17 | singleOf(::AppConfig) 18 | single { DatabaseFactoryImpl(get()) } 19 | single { JokeLocalDataSourceImpl() } 20 | single { JokeRepositoryImpl(get(), get()) } 21 | singleOf(::JobSchedulerManager) 22 | singleOf(::JobFactory) 23 | } 24 | -------------------------------------------------------------------------------- /part2/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | ${LOG_DEST}/ktor-chuck-norris-sample.log 10 | 11 | 12 | ${LOG_DEST}/ktor-chuck-norris-sample.%d{yyyy-MM-dd}.log 13 | 14 | 15 | ${LOG_MAX_HISTORY} 16 | 3GB 17 | 18 | 19 | 20 | 21 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /part3/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | ${LOG_DEST}/ktor-chuck-norris-sample.log 10 | 11 | 12 | ${LOG_DEST}/ktor-chuck-norris-sample.%d{yyyy-MM-dd}.log 13 | 14 | 15 | ${LOG_MAX_HISTORY} 16 | 3GB 17 | 18 | 19 | 20 | 21 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /part4/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | ${LOG_DEST}/ktor-chuck-norris-sample.log 10 | 11 | 12 | ${LOG_DEST}/ktor-chuck-norris-sample.%d{yyyy-MM-dd}.log 13 | 14 | 15 | ${LOG_MAX_HISTORY} 16 | 3GB 17 | 18 | 19 | 20 | 21 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /part5/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | ${LOG_DEST}/ktor-chuck-norris-sample.log 10 | 11 | 12 | ${LOG_DEST}/ktor-chuck-norris-sample.%d{yyyy-MM-dd}.log 13 | 14 | 15 | ${LOG_MAX_HISTORY} 16 | 3GB 17 | 18 | 19 | 20 | 21 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /part6/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | ${LOG_DEST}/ktor-chuck-norris-sample.log 10 | 11 | 12 | ${LOG_DEST}/ktor-chuck-norris-sample.%d{yyyy-MM-dd}.log 13 | 14 | 15 | ${LOG_MAX_HISTORY} 16 | 3GB 17 | 18 | 19 | 20 | 21 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | ${LOG_DEST}/ktor-chuck-norris-sample.log 10 | 11 | 12 | ${LOG_DEST}/ktor-chuck-norris-sample.%d{yyyy-MM-dd}.log 13 | 14 | 15 | ${LOG_MAX_HISTORY} 16 | 3GB 17 | 18 | 19 | 20 | 21 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /ktor-2.x/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | ${LOG_DEST}/ktor-chuck-norris-sample.log 10 | 11 | 12 | ${LOG_DEST}/ktor-chuck-norris-sample.%d{yyyy-MM-dd}.log 13 | 14 | 15 | ${LOG_MAX_HISTORY} 16 | 3GB 17 | 18 | 19 | 20 | 21 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /part3/src/main/kotlin/com/prof18/ktor/chucknorris/sample/config/AppConfig.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.config 2 | 3 | import io.ktor.application.* 4 | import org.koin.ktor.ext.inject 5 | 6 | class AppConfig { 7 | lateinit var databaseConfig: DatabaseConfig 8 | lateinit var serverConfig: ServerConfig 9 | // Place here other configurations 10 | } 11 | 12 | fun Application.setupConfig() { 13 | val appConfig by inject() 14 | 15 | // Server 16 | val serverObject = environment.config.config("ktor.server") 17 | val isProd = serverObject.property("isProd").getString().toBoolean() 18 | appConfig.serverConfig = ServerConfig(isProd) 19 | 20 | // Database 21 | val databaseObject = environment.config.config("ktor.database") 22 | val driverClass = databaseObject.property("driverClass").getString() 23 | val url = databaseObject.property("url").getString() 24 | val user = databaseObject.property("user").getString() 25 | val password = databaseObject.property("password").getString() 26 | val maxPoolSize = databaseObject.property("maxPoolSize").getString().toInt() 27 | appConfig.databaseConfig = DatabaseConfig(driverClass, url, user, password, maxPoolSize) 28 | } 29 | 30 | data class DatabaseConfig( 31 | val driverClass: String, 32 | val url: String, 33 | val user: String, 34 | val password: String, 35 | val maxPoolSize: Int 36 | ) 37 | 38 | data class ServerConfig( 39 | val isProd: Boolean 40 | ) 41 | -------------------------------------------------------------------------------- /part4/src/main/kotlin/com/prof18/ktor/chucknorris/sample/config/AppConfig.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.config 2 | 3 | import io.ktor.application.* 4 | import org.koin.ktor.ext.inject 5 | 6 | class AppConfig { 7 | lateinit var databaseConfig: DatabaseConfig 8 | lateinit var serverConfig: ServerConfig 9 | // Place here other configurations 10 | } 11 | 12 | fun Application.setupConfig() { 13 | val appConfig by inject() 14 | 15 | // Server 16 | val serverObject = environment.config.config("ktor.server") 17 | val isProd = serverObject.property("isProd").getString().toBoolean() 18 | appConfig.serverConfig = ServerConfig(isProd) 19 | 20 | // Database 21 | val databaseObject = environment.config.config("ktor.database") 22 | val driverClass = databaseObject.property("driverClass").getString() 23 | val url = databaseObject.property("url").getString() 24 | val user = databaseObject.property("user").getString() 25 | val password = databaseObject.property("password").getString() 26 | val maxPoolSize = databaseObject.property("maxPoolSize").getString().toInt() 27 | appConfig.databaseConfig = DatabaseConfig(driverClass, url, user, password, maxPoolSize) 28 | } 29 | 30 | data class DatabaseConfig( 31 | val driverClass: String, 32 | val url: String, 33 | val user: String, 34 | val password: String, 35 | val maxPoolSize: Int 36 | ) 37 | 38 | data class ServerConfig( 39 | val isProd: Boolean 40 | ) 41 | -------------------------------------------------------------------------------- /part5/src/main/kotlin/com/prof18/ktor/chucknorris/sample/config/AppConfig.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.config 2 | 3 | import io.ktor.application.* 4 | import org.koin.ktor.ext.inject 5 | 6 | class AppConfig { 7 | lateinit var databaseConfig: DatabaseConfig 8 | lateinit var serverConfig: ServerConfig 9 | // Place here other configurations 10 | } 11 | 12 | fun Application.setupConfig() { 13 | val appConfig by inject() 14 | 15 | // Server 16 | val serverObject = environment.config.config("ktor.server") 17 | val isProd = serverObject.property("isProd").getString().toBoolean() 18 | appConfig.serverConfig = ServerConfig(isProd) 19 | 20 | // Database 21 | val databaseObject = environment.config.config("ktor.database") 22 | val driverClass = databaseObject.property("driverClass").getString() 23 | val url = databaseObject.property("url").getString() 24 | val user = databaseObject.property("user").getString() 25 | val password = databaseObject.property("password").getString() 26 | val maxPoolSize = databaseObject.property("maxPoolSize").getString().toInt() 27 | appConfig.databaseConfig = DatabaseConfig(driverClass, url, user, password, maxPoolSize) 28 | } 29 | 30 | data class DatabaseConfig( 31 | val driverClass: String, 32 | val url: String, 33 | val user: String, 34 | val password: String, 35 | val maxPoolSize: Int 36 | ) 37 | 38 | data class ServerConfig( 39 | val isProd: Boolean 40 | ) 41 | -------------------------------------------------------------------------------- /part6/src/main/kotlin/com/prof18/ktor/chucknorris/sample/config/AppConfig.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.config 2 | 3 | import io.ktor.application.* 4 | import org.koin.ktor.ext.inject 5 | 6 | class AppConfig { 7 | lateinit var databaseConfig: DatabaseConfig 8 | lateinit var serverConfig: ServerConfig 9 | // Place here other configurations 10 | } 11 | 12 | fun Application.setupConfig() { 13 | val appConfig by inject() 14 | 15 | // Server 16 | val serverObject = environment.config.config("ktor.server") 17 | val isProd = serverObject.property("isProd").getString().toBoolean() 18 | appConfig.serverConfig = ServerConfig(isProd) 19 | 20 | // Database 21 | val databaseObject = environment.config.config("ktor.database") 22 | val driverClass = databaseObject.property("driverClass").getString() 23 | val url = databaseObject.property("url").getString() 24 | val user = databaseObject.property("user").getString() 25 | val password = databaseObject.property("password").getString() 26 | val maxPoolSize = databaseObject.property("maxPoolSize").getString().toInt() 27 | appConfig.databaseConfig = DatabaseConfig(driverClass, url, user, password, maxPoolSize) 28 | } 29 | 30 | data class DatabaseConfig( 31 | val driverClass: String, 32 | val url: String, 33 | val user: String, 34 | val password: String, 35 | val maxPoolSize: Int 36 | ) 37 | 38 | data class ServerConfig( 39 | val isProd: Boolean 40 | ) 41 | -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/config/AppConfig.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.config 2 | 3 | import io.ktor.application.* 4 | import org.koin.ktor.ext.inject 5 | 6 | class AppConfig { 7 | lateinit var databaseConfig: DatabaseConfig 8 | lateinit var serverConfig: ServerConfig 9 | // Place here other configurations 10 | } 11 | 12 | fun Application.setupConfig() { 13 | val appConfig by inject() 14 | 15 | // Server 16 | val serverObject = environment.config.config("ktor.server") 17 | val isProd = serverObject.property("isProd").getString().toBoolean() 18 | appConfig.serverConfig = ServerConfig(isProd) 19 | 20 | // Database 21 | val databaseObject = environment.config.config("ktor.database") 22 | val driverClass = databaseObject.property("driverClass").getString() 23 | val url = databaseObject.property("url").getString() 24 | val user = databaseObject.property("user").getString() 25 | val password = databaseObject.property("password").getString() 26 | val maxPoolSize = databaseObject.property("maxPoolSize").getString().toInt() 27 | appConfig.databaseConfig = DatabaseConfig(driverClass, url, user, password, maxPoolSize) 28 | } 29 | 30 | data class DatabaseConfig( 31 | val driverClass: String, 32 | val url: String, 33 | val user: String, 34 | val password: String, 35 | val maxPoolSize: Int 36 | ) 37 | 38 | data class ServerConfig( 39 | val isProd: Boolean 40 | ) 41 | -------------------------------------------------------------------------------- /ktor-2.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/config/AppConfig.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.config 2 | 3 | import io.ktor.server.application.Application 4 | import org.koin.ktor.ext.inject 5 | 6 | class AppConfig { 7 | lateinit var databaseConfig: DatabaseConfig 8 | lateinit var serverConfig: ServerConfig 9 | // Place here other configurations 10 | } 11 | 12 | fun Application.setupConfig() { 13 | val appConfig by inject() 14 | 15 | // Server 16 | val serverObject = environment.config.config("ktor.server") 17 | val isProd = serverObject.property("isProd").getString().toBoolean() 18 | appConfig.serverConfig = ServerConfig(isProd) 19 | 20 | // Database 21 | val databaseObject = environment.config.config("ktor.database") 22 | val driverClass = databaseObject.property("driverClass").getString() 23 | val url = databaseObject.property("url").getString() 24 | val user = databaseObject.property("user").getString() 25 | val password = databaseObject.property("password").getString() 26 | val maxPoolSize = databaseObject.property("maxPoolSize").getString().toInt() 27 | appConfig.databaseConfig = DatabaseConfig(driverClass, url, user, password, maxPoolSize) 28 | } 29 | 30 | data class DatabaseConfig( 31 | val driverClass: String, 32 | val url: String, 33 | val user: String, 34 | val password: String, 35 | val maxPoolSize: Int 36 | ) 37 | 38 | data class ServerConfig( 39 | val isProd: Boolean 40 | ) 41 | -------------------------------------------------------------------------------- /part1/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | val ktor_version: String by project 4 | val kotlin_version: String by project 5 | val logback_version: String by project 6 | val koin_version: String by project 7 | 8 | plugins { 9 | application 10 | kotlin("jvm") version "1.5.30" 11 | id("org.jetbrains.kotlin.plugin.serialization") version "1.5.30" 12 | } 13 | 14 | group = "com.prof18.ktor.chucknorris.sample" 15 | version = "0.0.1" 16 | application { 17 | mainClass.set("io.ktor.server.netty.EngineMain") 18 | } 19 | 20 | repositories { 21 | mavenCentral() 22 | } 23 | 24 | tasks.withType().all { 25 | kotlinOptions { 26 | jvmTarget = "11" 27 | } 28 | } 29 | 30 | dependencies { 31 | implementation("io.ktor:ktor-server-core:$ktor_version") 32 | implementation("io.ktor:ktor-serialization:$ktor_version") 33 | implementation("io.ktor:ktor-server-host-common:$ktor_version") 34 | implementation("io.ktor:ktor-server-netty:$ktor_version") 35 | implementation("io.ktor:ktor-locations:$ktor_version") 36 | implementation("ch.qos.logback:logback-classic:$logback_version") 37 | 38 | // Koin 39 | implementation("io.insert-koin:koin-ktor:$koin_version") 40 | implementation("io.insert-koin:koin-logger-slf4j:$koin_version") 41 | 42 | // Testing 43 | testImplementation("io.ktor:ktor-server-tests:$ktor_version") 44 | testImplementation("io.insert-koin:koin-test:$koin_version") 45 | testImplementation("io.insert-koin:koin-test-junit4:$koin_version") 46 | testImplementation("junit:junit:4.12") 47 | } -------------------------------------------------------------------------------- /part2/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | val ktor_version: String by project 4 | val kotlin_version: String by project 5 | val logback_version: String by project 6 | val koin_version: String by project 7 | 8 | plugins { 9 | application 10 | kotlin("jvm") version "1.5.30" 11 | id("org.jetbrains.kotlin.plugin.serialization") version "1.5.30" 12 | } 13 | 14 | group = "com.prof18.ktor.chucknorris.sample" 15 | version = "0.0.1" 16 | application { 17 | mainClass.set("io.ktor.server.netty.EngineMain") 18 | } 19 | 20 | repositories { 21 | mavenCentral() 22 | } 23 | 24 | tasks.withType().all { 25 | kotlinOptions { 26 | jvmTarget = "11" 27 | } 28 | } 29 | 30 | dependencies { 31 | implementation("io.ktor:ktor-server-core:$ktor_version") 32 | implementation("io.ktor:ktor-serialization:$ktor_version") 33 | implementation("io.ktor:ktor-server-host-common:$ktor_version") 34 | implementation("io.ktor:ktor-server-netty:$ktor_version") 35 | implementation("io.ktor:ktor-locations:$ktor_version") 36 | implementation("ch.qos.logback:logback-classic:$logback_version") 37 | 38 | // Koin 39 | implementation("io.insert-koin:koin-ktor:$koin_version") 40 | implementation("io.insert-koin:koin-logger-slf4j:$koin_version") 41 | 42 | // Testing 43 | testImplementation("io.ktor:ktor-server-tests:$ktor_version") 44 | testImplementation("io.insert-koin:koin-test:$koin_version") 45 | testImplementation("io.insert-koin:koin-test-junit4:$koin_version") 46 | testImplementation("junit:junit:4.12") 47 | } -------------------------------------------------------------------------------- /ktor-1.6.x/README.md: -------------------------------------------------------------------------------- 1 | # Ktor Chuck Norris Sample 2 | 3 | A [Ktor](http://ktor.io) (version 1.6.x) sample project that returns Random Chuck Norris jokes. 4 | 5 | This project contains all the code used in some articles published on my website. 6 | 7 | ## 1. Structuring a Ktor project 8 | 9 | - Article: https://www.marcogomiero.com/posts/2021/ktor-project-structure/ 10 | - Code: [part1 folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/part1) 11 | 12 | ## 2. How to persist Ktor logs 13 | 14 | - Article: https://www.marcogomiero.com/posts/2021/ktor-logging-on-disk/ 15 | - Code: [part2 folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/part2) 16 | 17 | ## 3. How to use an in-memory database for testing on Ktor 18 | 19 | - Article: https://www.marcogomiero.com/posts/2021/ktor-in-memory-db-testing/ 20 | - Code: [part3 folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/part3) 21 | 22 | ## 4. How to handle database migrations with Liquibase on Ktor 23 | 24 | - Article: https://www.marcogomiero.com/posts/2022/ktor-migration-liquibase/ 25 | - Code: [part4 folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/part4) 26 | 27 | ## 5. Generate API documentation from Swagger on Ktor 28 | 29 | - Article: https://www.marcogomiero.com/posts/2022/ktor-setup-documentation/ 30 | - Code: [part5 folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/part5) 31 | 32 | ## 6. How to schedule jobs with Quartz on Ktor 33 | 34 | - Article: https://www.marcogomiero.com/posts/2022/ktor-jobs-quartz/ 35 | - Code: [part6 folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/part6) 36 | 37 | --- 38 | 39 | The data used for the databases are from the [Chuck Norris IO](https://github.com/chucknorris-io/chuck-db) project. 40 | -------------------------------------------------------------------------------- /part3/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/TestServer.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSource 6 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSourceImpl 7 | import com.prof18.ktor.chucknorris.sample.module 8 | import com.prof18.ktor.chucknorris.sample.testutils.database.DatabaseFactoryForServerTest 9 | import io.ktor.config.* 10 | import io.ktor.locations.* 11 | import io.ktor.server.testing.* 12 | import org.koin.core.module.Module 13 | import org.koin.dsl.module 14 | import org.koin.dsl.single 15 | 16 | fun MapApplicationConfig.createConfigForTesting() { 17 | // Server config 18 | put("ktor.server.isProd", "false") 19 | 20 | // Database Config 21 | put("ktor.database.driverClass", "org.h2.Driver") 22 | put("ktor.database.url", "jdbc:h2:mem:;DATABASE_TO_UPPER=false;MODE=MYSQL") 23 | put("ktor.database.user", "root") 24 | put("ktor.database.password", "password") 25 | put("ktor.database.maxPoolSize", "1") 26 | } 27 | 28 | 29 | @KtorExperimentalLocationsAPI 30 | fun withTestServer(koinModules: List = listOf(appTestModule), block: TestApplicationEngine.() -> Unit) { 31 | withTestApplication( 32 | { 33 | (environment.config as MapApplicationConfig).apply { 34 | createConfigForTesting() 35 | } 36 | module(testing = true, koinModules = koinModules) 37 | }, block 38 | ) 39 | } 40 | 41 | val appTestModule = module { 42 | single() 43 | single { DatabaseFactoryForServerTest(get()) } 44 | single { JokeLocalDataSourceImpl() } 45 | } 46 | 47 | 48 | -------------------------------------------------------------------------------- /part4/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/TestServer.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSource 6 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSourceImpl 7 | import com.prof18.ktor.chucknorris.sample.module 8 | import com.prof18.ktor.chucknorris.sample.testutils.database.DatabaseFactoryForServerTest 9 | import io.ktor.config.* 10 | import io.ktor.locations.* 11 | import io.ktor.server.testing.* 12 | import org.koin.core.module.Module 13 | import org.koin.dsl.module 14 | import org.koin.dsl.single 15 | 16 | fun MapApplicationConfig.createConfigForTesting() { 17 | // Server config 18 | put("ktor.server.isProd", "false") 19 | 20 | // Database Config 21 | put("ktor.database.driverClass", "org.h2.Driver") 22 | put("ktor.database.url", "jdbc:h2:mem:;DATABASE_TO_UPPER=false;MODE=MYSQL") 23 | put("ktor.database.user", "root") 24 | put("ktor.database.password", "password") 25 | put("ktor.database.maxPoolSize", "1") 26 | } 27 | 28 | 29 | @KtorExperimentalLocationsAPI 30 | fun withTestServer(koinModules: List = listOf(appTestModule), block: TestApplicationEngine.() -> Unit) { 31 | withTestApplication( 32 | { 33 | (environment.config as MapApplicationConfig).apply { 34 | createConfigForTesting() 35 | } 36 | module(testing = true, koinModules = koinModules) 37 | }, block 38 | ) 39 | } 40 | 41 | val appTestModule = module { 42 | single() 43 | single { DatabaseFactoryForServerTest(get()) } 44 | single { JokeLocalDataSourceImpl() } 45 | } 46 | 47 | 48 | -------------------------------------------------------------------------------- /part5/src/test/kotlin/com/prof18/ktor/chucknorris/sample/testutils/TestServer.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.testutils 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSource 6 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSourceImpl 7 | import com.prof18.ktor.chucknorris.sample.module 8 | import com.prof18.ktor.chucknorris.sample.testutils.database.DatabaseFactoryForServerTest 9 | import io.ktor.config.* 10 | import io.ktor.locations.* 11 | import io.ktor.server.testing.* 12 | import org.koin.core.module.Module 13 | import org.koin.dsl.module 14 | import org.koin.dsl.single 15 | 16 | fun MapApplicationConfig.createConfigForTesting() { 17 | // Server config 18 | put("ktor.server.isProd", "false") 19 | 20 | // Database Config 21 | put("ktor.database.driverClass", "org.h2.Driver") 22 | put("ktor.database.url", "jdbc:h2:mem:;DATABASE_TO_UPPER=false;MODE=MYSQL") 23 | put("ktor.database.user", "root") 24 | put("ktor.database.password", "password") 25 | put("ktor.database.maxPoolSize", "1") 26 | } 27 | 28 | 29 | @KtorExperimentalLocationsAPI 30 | fun withTestServer(koinModules: List = listOf(appTestModule), block: TestApplicationEngine.() -> Unit) { 31 | withTestApplication( 32 | { 33 | (environment.config as MapApplicationConfig).apply { 34 | createConfigForTesting() 35 | } 36 | module(testing = true, koinModules = koinModules) 37 | }, block 38 | ) 39 | } 40 | 41 | val appTestModule = module { 42 | single() 43 | single { DatabaseFactoryForServerTest(get()) } 44 | single { JokeLocalDataSourceImpl() } 45 | } 46 | 47 | 48 | -------------------------------------------------------------------------------- /ktor-2.x/README.md: -------------------------------------------------------------------------------- 1 | # Ktor Chuck Norris Sample 2 | 3 | A [Ktor](http://ktor.io) (version 2.x) sample project that returns Random Chuck Norris jokes. 4 | 5 | This project contains all the code used in some articles published on my website. 6 | 7 | ## 1. Structuring a Ktor project 8 | 9 | - Article: https://www.marcogomiero.com/posts/2021/ktor-project-structure/ 10 | - Code: [part1 folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/part1) 11 | 12 | ## 2. How to persist Ktor logs 13 | 14 | - Article: https://www.marcogomiero.com/posts/2021/ktor-logging-on-disk/ 15 | - Code: [part2 folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/part2) 16 | 17 | ## 3. How to use an in-memory database for testing on Ktor 18 | 19 | - Article: https://www.marcogomiero.com/posts/2021/ktor-in-memory-db-testing/ 20 | - Code: [part3 folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/part3) 21 | 22 | ## 4. How to handle database migrations with Liquibase on Ktor 23 | 24 | - Article: https://www.marcogomiero.com/posts/2022/ktor-migration-liquibase/ 25 | - Code: [part4 folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/part4) 26 | 27 | ## 5. Generate API documentation from Swagger on Ktor 28 | 29 | - Article: https://www.marcogomiero.com/posts/2022/ktor-setup-documentation/ 30 | - Code: [part5 folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/part5) 31 | 32 | ## 6. How to schedule jobs with Quartz on Ktor 33 | 34 | - Article: https://www.marcogomiero.com/posts/2022/ktor-jobs-quartz/ 35 | - Code: [part6 folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/part6) 36 | 37 | ## Ktor 1.6.x 38 | 39 | In the [ktor-1.6.x folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/ktor-1.6.x), the entire project is implemented with Ktor 1.6.x. 40 | 41 | --- 42 | 43 | The data used for the databases are from the [Chuck Norris IO](https://github.com/chucknorris-io/chuck-db) project. 44 | -------------------------------------------------------------------------------- /part1/src/test/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/resource/JokeResourceTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.resource 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.fake.JokeRepositoryFake 6 | import com.prof18.ktor.chucknorris.sample.testutils.appTestModule 7 | import com.prof18.ktor.chucknorris.sample.testutils.withTestServer 8 | import io.ktor.http.* 9 | import io.ktor.locations.* 10 | import io.ktor.server.testing.* 11 | import kotlinx.serialization.ExperimentalSerializationApi 12 | import kotlinx.serialization.decodeFromString 13 | import kotlinx.serialization.json.Json 14 | import org.junit.Assert.assertEquals 15 | import org.junit.Test 16 | import org.koin.dsl.module 17 | import org.koin.test.AutoCloseKoinTest 18 | 19 | @ExperimentalSerializationApi 20 | @KtorExperimentalLocationsAPI 21 | class JokeResourceTest : AutoCloseKoinTest() { 22 | 23 | @Test 24 | fun `random joke api works correctly`() = withTestServer( 25 | koinModules = appTestModule.plus( 26 | module { 27 | // Just to showcase the possibility, in this case this dependency can be put in the base test module 28 | single { JokeRepositoryFake() } 29 | } 30 | ) 31 | ) { 32 | 33 | val href = application.locations.href( 34 | JokeEndpoint.Random( 35 | parent = JokeEndpoint() 36 | ) 37 | ) 38 | 39 | handleRequest(HttpMethod.Get, href).apply { 40 | assertEquals(HttpStatusCode.OK, response.status()) 41 | 42 | val response = Json.decodeFromString(response.content!!) 43 | 44 | assertEquals("fake-id", response.jokeId) 45 | assertEquals("fake-content", response.jokeContent) 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /ktor-2.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/database/DatabaseFactoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.zaxxer.hikari.HikariConfig 5 | import com.zaxxer.hikari.HikariDataSource 6 | import org.jetbrains.exposed.sql.Database 7 | 8 | class DatabaseFactoryImpl(appConfig: AppConfig) : DatabaseFactory { 9 | 10 | private val dbConfig = appConfig.databaseConfig 11 | 12 | override fun close() { 13 | // not necessary 14 | } 15 | 16 | override fun connect() { 17 | Database.connect(hikari()) 18 | } 19 | 20 | private fun hikari(): HikariDataSource { 21 | val config = HikariConfig() 22 | config.driverClassName = dbConfig.driverClass 23 | config.jdbcUrl = dbConfig.url 24 | config.username = dbConfig.user 25 | config.password = dbConfig.password 26 | config.maximumPoolSize = dbConfig.maxPoolSize 27 | config.isAutoCommit = false 28 | config.transactionIsolation = "TRANSACTION_REPEATABLE_READ" 29 | 30 | // Suggestions from https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration 31 | config.addDataSourceProperty("cachePrepStmts", "true"); 32 | config.addDataSourceProperty("prepStmtCacheSize", "250"); 33 | config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); 34 | config.addDataSourceProperty("useServerPrepStmts", "true"); 35 | config.addDataSourceProperty("useLocalSessionState", "true"); 36 | config.addDataSourceProperty("rewriteBatchedStatements", "true"); 37 | config.addDataSourceProperty("cacheResultSetMetadata", "true"); 38 | config.addDataSourceProperty("cacheServerConfiguration", "true"); 39 | config.addDataSourceProperty("elideSetAutoCommits", "true"); 40 | config.addDataSourceProperty("maintainTimeStats", "false"); 41 | 42 | config.validate() 43 | return HikariDataSource(config) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /part2/src/test/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/resource/JokeResourceTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.resource 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.fake.JokeRepositoryFake 6 | import com.prof18.ktor.chucknorris.sample.testutils.appTestModule 7 | import com.prof18.ktor.chucknorris.sample.testutils.withTestServer 8 | import io.ktor.http.* 9 | import io.ktor.locations.* 10 | import io.ktor.server.testing.* 11 | import io.ktor.util.* 12 | import kotlinx.serialization.ExperimentalSerializationApi 13 | import kotlinx.serialization.decodeFromString 14 | import kotlinx.serialization.json.Json 15 | import org.junit.Assert.assertEquals 16 | import org.junit.Test 17 | import org.koin.dsl.module 18 | import org.koin.test.AutoCloseKoinTest 19 | 20 | @KtorExperimentalLocationsAPI 21 | class JokeResourceTest : AutoCloseKoinTest() { 22 | 23 | @ExperimentalSerializationApi 24 | @Test 25 | fun `random joke api works correctly`() = withTestServer( 26 | koinModules = appTestModule.plus( 27 | module { 28 | // Just to showcase the possibility, in this case this dependency can be put in the base test module 29 | single { JokeRepositoryFake() } 30 | } 31 | ) 32 | ) { 33 | 34 | val href = application.locations.href( 35 | JokeEndpoint.Random( 36 | parent = JokeEndpoint() 37 | ) 38 | ) 39 | 40 | handleRequest(HttpMethod.Get, href).apply { 41 | assertEquals(HttpStatusCode.OK, response.status()) 42 | 43 | val response = Json.decodeFromString(response.content!!) 44 | 45 | assertEquals("fake-id", response.jokeId) 46 | assertEquals("fake-content", response.jokeContent) 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /part3/src/main/kotlin/com/prof18/ktor/chucknorris/sample/database/DatabaseFactoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.zaxxer.hikari.HikariConfig 5 | import com.zaxxer.hikari.HikariDataSource 6 | import io.ktor.config.* 7 | import org.jetbrains.exposed.sql.Database 8 | 9 | class DatabaseFactoryImpl(appConfig: AppConfig) : DatabaseFactory { 10 | 11 | private val dbConfig = appConfig.databaseConfig 12 | 13 | override fun close() { 14 | // not necessary 15 | } 16 | 17 | override fun connect() { 18 | Database.connect(hikari()) 19 | } 20 | 21 | private fun hikari(): HikariDataSource { 22 | val config = HikariConfig() 23 | config.driverClassName = dbConfig.driverClass 24 | config.jdbcUrl = dbConfig.url 25 | config.username = dbConfig.user 26 | config.password = dbConfig.password 27 | config.maximumPoolSize = dbConfig.maxPoolSize 28 | config.isAutoCommit = false 29 | config.transactionIsolation = "TRANSACTION_REPEATABLE_READ" 30 | 31 | // Suggestions from https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration 32 | config.addDataSourceProperty("cachePrepStmts", "true"); 33 | config.addDataSourceProperty("prepStmtCacheSize", "250"); 34 | config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); 35 | config.addDataSourceProperty("useServerPrepStmts", "true"); 36 | config.addDataSourceProperty("useLocalSessionState", "true"); 37 | config.addDataSourceProperty("rewriteBatchedStatements", "true"); 38 | config.addDataSourceProperty("cacheResultSetMetadata", "true"); 39 | config.addDataSourceProperty("cacheServerConfiguration", "true"); 40 | config.addDataSourceProperty("elideSetAutoCommits", "true"); 41 | config.addDataSourceProperty("maintainTimeStats", "false"); 42 | 43 | config.validate() 44 | return HikariDataSource(config) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /part4/src/main/kotlin/com/prof18/ktor/chucknorris/sample/database/DatabaseFactoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.zaxxer.hikari.HikariConfig 5 | import com.zaxxer.hikari.HikariDataSource 6 | import io.ktor.config.* 7 | import org.jetbrains.exposed.sql.Database 8 | 9 | class DatabaseFactoryImpl(appConfig: AppConfig) : DatabaseFactory { 10 | 11 | private val dbConfig = appConfig.databaseConfig 12 | 13 | override fun close() { 14 | // not necessary 15 | } 16 | 17 | override fun connect() { 18 | Database.connect(hikari()) 19 | } 20 | 21 | private fun hikari(): HikariDataSource { 22 | val config = HikariConfig() 23 | config.driverClassName = dbConfig.driverClass 24 | config.jdbcUrl = dbConfig.url 25 | config.username = dbConfig.user 26 | config.password = dbConfig.password 27 | config.maximumPoolSize = dbConfig.maxPoolSize 28 | config.isAutoCommit = false 29 | config.transactionIsolation = "TRANSACTION_REPEATABLE_READ" 30 | 31 | // Suggestions from https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration 32 | config.addDataSourceProperty("cachePrepStmts", "true"); 33 | config.addDataSourceProperty("prepStmtCacheSize", "250"); 34 | config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); 35 | config.addDataSourceProperty("useServerPrepStmts", "true"); 36 | config.addDataSourceProperty("useLocalSessionState", "true"); 37 | config.addDataSourceProperty("rewriteBatchedStatements", "true"); 38 | config.addDataSourceProperty("cacheResultSetMetadata", "true"); 39 | config.addDataSourceProperty("cacheServerConfiguration", "true"); 40 | config.addDataSourceProperty("elideSetAutoCommits", "true"); 41 | config.addDataSourceProperty("maintainTimeStats", "false"); 42 | 43 | config.validate() 44 | return HikariDataSource(config) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /part5/src/main/kotlin/com/prof18/ktor/chucknorris/sample/database/DatabaseFactoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.zaxxer.hikari.HikariConfig 5 | import com.zaxxer.hikari.HikariDataSource 6 | import io.ktor.config.* 7 | import org.jetbrains.exposed.sql.Database 8 | 9 | class DatabaseFactoryImpl(appConfig: AppConfig) : DatabaseFactory { 10 | 11 | private val dbConfig = appConfig.databaseConfig 12 | 13 | override fun close() { 14 | // not necessary 15 | } 16 | 17 | override fun connect() { 18 | Database.connect(hikari()) 19 | } 20 | 21 | private fun hikari(): HikariDataSource { 22 | val config = HikariConfig() 23 | config.driverClassName = dbConfig.driverClass 24 | config.jdbcUrl = dbConfig.url 25 | config.username = dbConfig.user 26 | config.password = dbConfig.password 27 | config.maximumPoolSize = dbConfig.maxPoolSize 28 | config.isAutoCommit = false 29 | config.transactionIsolation = "TRANSACTION_REPEATABLE_READ" 30 | 31 | // Suggestions from https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration 32 | config.addDataSourceProperty("cachePrepStmts", "true"); 33 | config.addDataSourceProperty("prepStmtCacheSize", "250"); 34 | config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); 35 | config.addDataSourceProperty("useServerPrepStmts", "true"); 36 | config.addDataSourceProperty("useLocalSessionState", "true"); 37 | config.addDataSourceProperty("rewriteBatchedStatements", "true"); 38 | config.addDataSourceProperty("cacheResultSetMetadata", "true"); 39 | config.addDataSourceProperty("cacheServerConfiguration", "true"); 40 | config.addDataSourceProperty("elideSetAutoCommits", "true"); 41 | config.addDataSourceProperty("maintainTimeStats", "false"); 42 | 43 | config.validate() 44 | return HikariDataSource(config) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /part6/src/main/kotlin/com/prof18/ktor/chucknorris/sample/database/DatabaseFactoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.zaxxer.hikari.HikariConfig 5 | import com.zaxxer.hikari.HikariDataSource 6 | import io.ktor.config.* 7 | import org.jetbrains.exposed.sql.Database 8 | 9 | class DatabaseFactoryImpl(appConfig: AppConfig) : DatabaseFactory { 10 | 11 | private val dbConfig = appConfig.databaseConfig 12 | 13 | override fun close() { 14 | // not necessary 15 | } 16 | 17 | override fun connect() { 18 | Database.connect(hikari()) 19 | } 20 | 21 | private fun hikari(): HikariDataSource { 22 | val config = HikariConfig() 23 | config.driverClassName = dbConfig.driverClass 24 | config.jdbcUrl = dbConfig.url 25 | config.username = dbConfig.user 26 | config.password = dbConfig.password 27 | config.maximumPoolSize = dbConfig.maxPoolSize 28 | config.isAutoCommit = false 29 | config.transactionIsolation = "TRANSACTION_REPEATABLE_READ" 30 | 31 | // Suggestions from https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration 32 | config.addDataSourceProperty("cachePrepStmts", "true"); 33 | config.addDataSourceProperty("prepStmtCacheSize", "250"); 34 | config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); 35 | config.addDataSourceProperty("useServerPrepStmts", "true"); 36 | config.addDataSourceProperty("useLocalSessionState", "true"); 37 | config.addDataSourceProperty("rewriteBatchedStatements", "true"); 38 | config.addDataSourceProperty("cacheResultSetMetadata", "true"); 39 | config.addDataSourceProperty("cacheServerConfiguration", "true"); 40 | config.addDataSourceProperty("elideSetAutoCommits", "true"); 41 | config.addDataSourceProperty("maintainTimeStats", "false"); 42 | 43 | config.validate() 44 | return HikariDataSource(config) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/database/DatabaseFactoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.database 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import com.zaxxer.hikari.HikariConfig 5 | import com.zaxxer.hikari.HikariDataSource 6 | import io.ktor.config.* 7 | import org.jetbrains.exposed.sql.Database 8 | 9 | class DatabaseFactoryImpl(appConfig: AppConfig) : DatabaseFactory { 10 | 11 | private val dbConfig = appConfig.databaseConfig 12 | 13 | override fun close() { 14 | // not necessary 15 | } 16 | 17 | override fun connect() { 18 | Database.connect(hikari()) 19 | } 20 | 21 | private fun hikari(): HikariDataSource { 22 | val config = HikariConfig() 23 | config.driverClassName = dbConfig.driverClass 24 | config.jdbcUrl = dbConfig.url 25 | config.username = dbConfig.user 26 | config.password = dbConfig.password 27 | config.maximumPoolSize = dbConfig.maxPoolSize 28 | config.isAutoCommit = false 29 | config.transactionIsolation = "TRANSACTION_REPEATABLE_READ" 30 | 31 | // Suggestions from https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration 32 | config.addDataSourceProperty("cachePrepStmts", "true"); 33 | config.addDataSourceProperty("prepStmtCacheSize", "250"); 34 | config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); 35 | config.addDataSourceProperty("useServerPrepStmts", "true"); 36 | config.addDataSourceProperty("useLocalSessionState", "true"); 37 | config.addDataSourceProperty("rewriteBatchedStatements", "true"); 38 | config.addDataSourceProperty("cacheResultSetMetadata", "true"); 39 | config.addDataSourceProperty("cacheServerConfiguration", "true"); 40 | config.addDataSourceProperty("elideSetAutoCommits", "true"); 41 | config.addDataSourceProperty("maintainTimeStats", "false"); 42 | 43 | config.validate() 44 | return HikariDataSource(config) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ktor-2.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/jobs/JobSchedulerManager.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.jobs 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import org.quartz.Scheduler 5 | import org.quartz.SchedulerFactory 6 | import org.quartz.impl.StdSchedulerFactory 7 | import java.util.* 8 | 9 | class JobSchedulerManager(appConfig: AppConfig) { 10 | 11 | var scheduler: Scheduler 12 | 13 | init { 14 | val databaseConfig = appConfig.databaseConfig 15 | 16 | val props = Properties() 17 | props["org.quartz.scheduler.instanceName"] = "ChuckNorrisScheduler" 18 | props["org.quartz.threadPool.threadCount"] = "3" 19 | 20 | props["org.quartz.jobStore.dataSource"] = "mySql" 21 | props["org.quartz.dataSource.mySql.driver"] = databaseConfig.driverClass 22 | props["org.quartz.dataSource.mySql.URL"] = databaseConfig.url 23 | props["org.quartz.dataSource.mySql.user"] = databaseConfig.user 24 | props["org.quartz.dataSource.mySql.password"] = databaseConfig.password 25 | props["org.quartz.dataSource.mySql.maxConnections"] = "10" 26 | 27 | props["org.quartz.jobStore.class"] = "org.quartz.impl.jdbcjobstore.JobStoreTX" 28 | props["org.quartz.jobStore.driverDelegateClass"] = "org.quartz.impl.jdbcjobstore.StdJDBCDelegate" 29 | props["org.quartz.jobStore.tablePrefix"] = "QRTZ_" 30 | 31 | 32 | props["org.quartz.plugin.triggHistory.class"] = "org.quartz.plugins.history.LoggingTriggerHistoryPlugin" 33 | props["org.quartz.plugin.triggHistory.triggerFiredMessage"] = """Trigger {1}.{0} fired job {6}.{5} at: {4, date, HH:mm:ss MM/dd/yyyy}""" 34 | props["org.quartz.plugin.triggHistory.triggerCompleteMessage"] = """Trigger {1}.{0} completed firing job {6}.{5} at {4, date, HH:mm:ss MM/dd/yyyy}""" 35 | 36 | val schedulerFactory: SchedulerFactory = StdSchedulerFactory(props) 37 | scheduler = schedulerFactory.scheduler 38 | } 39 | 40 | fun startScheduler() { 41 | scheduler.start() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /part6/src/main/kotlin/com/prof18/ktor/chucknorris/sample/jobs/JobSchedulerManager.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.jobs 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import org.quartz.Scheduler 5 | import org.quartz.SchedulerFactory 6 | import org.quartz.impl.StdSchedulerFactory 7 | import java.util.* 8 | 9 | class JobSchedulerManager(appConfig: AppConfig) { 10 | 11 | var scheduler: Scheduler 12 | 13 | init { 14 | val databaseConfig = appConfig.databaseConfig 15 | 16 | val props = Properties() 17 | props["org.quartz.scheduler.instanceName"] = "ChuckNorrisScheduler" 18 | props["org.quartz.threadPool.threadCount"] = "3" 19 | 20 | props["org.quartz.jobStore.dataSource"] = "mySql" 21 | props["org.quartz.dataSource.mySql.driver"] = databaseConfig.driverClass 22 | props["org.quartz.dataSource.mySql.URL"] = databaseConfig.url 23 | props["org.quartz.dataSource.mySql.user"] = databaseConfig.user 24 | props["org.quartz.dataSource.mySql.password"] = databaseConfig.password 25 | props["org.quartz.dataSource.mySql.maxConnections"] = "10" 26 | 27 | props["org.quartz.jobStore.class"] = "org.quartz.impl.jdbcjobstore.JobStoreTX" 28 | props["org.quartz.jobStore.driverDelegateClass"] = "org.quartz.impl.jdbcjobstore.StdJDBCDelegate" 29 | props["org.quartz.jobStore.tablePrefix"] = "QRTZ_" 30 | 31 | 32 | props["org.quartz.plugin.triggHistory.class"] = "org.quartz.plugins.history.LoggingTriggerHistoryPlugin" 33 | props["org.quartz.plugin.triggHistory.triggerFiredMessage"] = """Trigger {1}.{0} fired job {6}.{5} at: {4, date, HH:mm:ss MM/dd/yyyy}""" 34 | props["org.quartz.plugin.triggHistory.triggerCompleteMessage"] = """Trigger {1}.{0} completed firing job {6}.{5} at {4, date, HH:mm:ss MM/dd/yyyy}""" 35 | 36 | val schedulerFactory: SchedulerFactory = StdSchedulerFactory(props) 37 | scheduler = schedulerFactory.scheduler 38 | } 39 | 40 | fun startScheduler() { 41 | scheduler.start() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/jobs/JobSchedulerManager.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.jobs 2 | 3 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 4 | import org.quartz.Scheduler 5 | import org.quartz.SchedulerFactory 6 | import org.quartz.impl.StdSchedulerFactory 7 | import java.util.* 8 | 9 | class JobSchedulerManager(appConfig: AppConfig) { 10 | 11 | var scheduler: Scheduler 12 | 13 | init { 14 | val databaseConfig = appConfig.databaseConfig 15 | 16 | val props = Properties() 17 | props["org.quartz.scheduler.instanceName"] = "ChuckNorrisScheduler" 18 | props["org.quartz.threadPool.threadCount"] = "3" 19 | 20 | props["org.quartz.jobStore.dataSource"] = "mySql" 21 | props["org.quartz.dataSource.mySql.driver"] = databaseConfig.driverClass 22 | props["org.quartz.dataSource.mySql.URL"] = databaseConfig.url 23 | props["org.quartz.dataSource.mySql.user"] = databaseConfig.user 24 | props["org.quartz.dataSource.mySql.password"] = databaseConfig.password 25 | props["org.quartz.dataSource.mySql.maxConnections"] = "10" 26 | 27 | props["org.quartz.jobStore.class"] = "org.quartz.impl.jdbcjobstore.JobStoreTX" 28 | props["org.quartz.jobStore.driverDelegateClass"] = "org.quartz.impl.jdbcjobstore.StdJDBCDelegate" 29 | props["org.quartz.jobStore.tablePrefix"] = "QRTZ_" 30 | 31 | 32 | props["org.quartz.plugin.triggHistory.class"] = "org.quartz.plugins.history.LoggingTriggerHistoryPlugin" 33 | props["org.quartz.plugin.triggHistory.triggerFiredMessage"] = """Trigger {1}.{0} fired job {6}.{5} at: {4, date, HH:mm:ss MM/dd/yyyy}""" 34 | props["org.quartz.plugin.triggHistory.triggerCompleteMessage"] = """Trigger {1}.{0} completed firing job {6}.{5} at {4, date, HH:mm:ss MM/dd/yyyy}""" 35 | 36 | val schedulerFactory: SchedulerFactory = StdSchedulerFactory(props) 37 | scheduler = schedulerFactory.scheduler 38 | } 39 | 40 | fun startScheduler() { 41 | scheduler.start() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ktor Chuck Norris Sample 2 | 3 | A [Ktor](http://ktor.io) sample project that returns Random Chuck Norris jokes. 4 | 5 | This project contains all the code used in some articles published on my website. 6 | 7 | ## 1. Structuring a Ktor project 8 | 9 | - Article: https://www.marcogomiero.com/posts/2021/ktor-project-structure/ 10 | - Code: [part1 folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/part1) 11 | 12 | ## 2. How to persist Ktor logs 13 | 14 | - Article: https://www.marcogomiero.com/posts/2021/ktor-logging-on-disk/ 15 | - Code: [part2 folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/part2) 16 | 17 | ## 3. How to use an in-memory database for testing on Ktor 18 | 19 | - Article: https://www.marcogomiero.com/posts/2021/ktor-in-memory-db-testing/ 20 | - Code: [part3 folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/part3) 21 | 22 | ## 4. How to handle database migrations with Liquibase on Ktor 23 | 24 | - Article: https://www.marcogomiero.com/posts/2022/ktor-migration-liquibase/ 25 | - Code: [part4 folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/part4) 26 | 27 | ## 5. Generate API documentation from Swagger on Ktor 28 | 29 | - Article: https://www.marcogomiero.com/posts/2022/ktor-setup-documentation/ 30 | - Code: [part5 folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/part5) 31 | 32 | ## 6. How to schedule jobs with Quartz on Ktor 33 | 34 | - Article: https://www.marcogomiero.com/posts/2022/ktor-jobs-quartz/ 35 | - Code: [part6 folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/part6) 36 | 37 | ## Ktor 1.6.x 38 | 39 | In the [ktor-1.6.x folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/ktor-1.6.x), the entire project is implemented with Ktor 1.6.x. 40 | 41 | ## Ktor 2.x 42 | 43 | In the [ktor-2.x folder](https://github.com/prof18/ktor-chuck-norris-sample/tree/main/ktor-2.x), the entire project is implemented with Ktor 1.6.x. 44 | 45 | --- 46 | 47 | The data used for the databases are from the [Chuck Norris IO](https://github.com/chucknorris-io/chuck-db) project. 48 | -------------------------------------------------------------------------------- /part1/src/main/kotlin/com/prof18/ktor/chucknorris/sample/Application.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample 2 | 3 | import ch.qos.logback.classic.Logger 4 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 5 | import com.prof18.ktor.chucknorris.sample.config.setupConfig 6 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 7 | import com.prof18.ktor.chucknorris.sample.di.appModule 8 | import com.prof18.ktor.chucknorris.sample.features.jokes.resource.jokeEndpoint 9 | import io.ktor.application.* 10 | import io.ktor.features.* 11 | import io.ktor.http.content.* 12 | import io.ktor.locations.* 13 | import io.ktor.response.* 14 | import io.ktor.routing.* 15 | import io.ktor.serialization.* 16 | import org.koin.core.module.Module 17 | import org.koin.ktor.ext.Koin 18 | import org.koin.ktor.ext.inject 19 | import org.koin.logger.slf4jLogger 20 | import org.slf4j.LoggerFactory 21 | import org.slf4j.event.Level 22 | 23 | fun main(args: Array): Unit = 24 | io.ktor.server.netty.EngineMain.main(args) 25 | 26 | /** 27 | * Please note that you can use any other name instead of *module*. 28 | * Also note that you can have more then one modules in your application. 29 | * */ 30 | @KtorExperimentalLocationsAPI 31 | @Suppress("unused") // Referenced in application.conf 32 | @kotlin.jvm.JvmOverloads 33 | fun Application.module(testing: Boolean = false, koinModules: List = listOf(appModule)) { 34 | 35 | install(Koin) { 36 | slf4jLogger() 37 | modules(koinModules) 38 | } 39 | 40 | setupConfig() 41 | 42 | val appConfig by inject() 43 | 44 | if (!appConfig.serverConfig.isProd) { 45 | val root = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME) as Logger 46 | root.level = ch.qos.logback.classic.Level.TRACE 47 | } 48 | 49 | install(ContentNegotiation) { 50 | json() 51 | } 52 | 53 | install(CallLogging) { 54 | level = Level.INFO 55 | } 56 | 57 | install(Locations) 58 | 59 | routing { 60 | jokeEndpoint() 61 | get("/") { 62 | call.respondText("This is a sample Ktor backend to get Chuck Norris jokes") 63 | } 64 | } 65 | } 66 | 67 | 68 | -------------------------------------------------------------------------------- /part2/src/main/kotlin/com/prof18/ktor/chucknorris/sample/Application.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample 2 | 3 | import ch.qos.logback.classic.Logger 4 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 5 | import com.prof18.ktor.chucknorris.sample.config.setupConfig 6 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 7 | import com.prof18.ktor.chucknorris.sample.di.appModule 8 | import com.prof18.ktor.chucknorris.sample.features.jokes.resource.jokeEndpoint 9 | import io.ktor.application.* 10 | import io.ktor.features.* 11 | import io.ktor.http.content.* 12 | import io.ktor.locations.* 13 | import io.ktor.response.* 14 | import io.ktor.routing.* 15 | import io.ktor.serialization.* 16 | import org.koin.core.module.Module 17 | import org.koin.ktor.ext.Koin 18 | import org.koin.ktor.ext.inject 19 | import org.koin.logger.slf4jLogger 20 | import org.slf4j.LoggerFactory 21 | import org.slf4j.event.Level 22 | 23 | fun main(args: Array): Unit = 24 | io.ktor.server.netty.EngineMain.main(args) 25 | 26 | /** 27 | * Please note that you can use any other name instead of *module*. 28 | * Also note that you can have more then one modules in your application. 29 | * */ 30 | @KtorExperimentalLocationsAPI 31 | @Suppress("unused") // Referenced in application.conf 32 | @kotlin.jvm.JvmOverloads 33 | fun Application.module(testing: Boolean = false, koinModules: List = listOf(appModule)) { 34 | 35 | install(Koin) { 36 | slf4jLogger() 37 | modules(koinModules) 38 | } 39 | 40 | setupConfig() 41 | 42 | val appConfig by inject() 43 | 44 | if (!appConfig.serverConfig.isProd) { 45 | val root = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME) as Logger 46 | root.level = ch.qos.logback.classic.Level.TRACE 47 | } 48 | 49 | install(ContentNegotiation) { 50 | json() 51 | } 52 | 53 | install(CallLogging) { 54 | level = Level.INFO 55 | } 56 | 57 | install(Locations) 58 | 59 | routing { 60 | jokeEndpoint() 61 | get("/") { 62 | call.respondText("This is a sample Ktor backend to get Chuck Norris jokes") 63 | } 64 | } 65 | } 66 | 67 | 68 | -------------------------------------------------------------------------------- /ktor-2.x/src/test/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/resource/JokeResourceTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.resource 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepositoryImpl 6 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 7 | import com.prof18.ktor.chucknorris.sample.testutils.appTestModule 8 | import com.prof18.ktor.chucknorris.sample.testutils.withTestServer 9 | import io.ktor.client.call.body 10 | import io.ktor.client.request.get 11 | import io.ktor.http.HttpStatusCode 12 | import kotlinx.serialization.ExperimentalSerializationApi 13 | import kotlinx.serialization.decodeFromString 14 | import kotlinx.serialization.json.Json 15 | import org.jetbrains.exposed.sql.transactions.transaction 16 | import org.junit.Assert.assertEquals 17 | import org.junit.Test 18 | import org.koin.dsl.module 19 | import org.koin.test.AutoCloseKoinTest 20 | import java.time.LocalDateTime 21 | 22 | @ExperimentalSerializationApi 23 | class JokeResourceTest : AutoCloseKoinTest() { 24 | 25 | @Test 26 | fun `random joke api works correctly`() = withTestServer( 27 | koinModules = appTestModule.plus( 28 | module { 29 | // Just to showcase the possibility, in this case this dependency can be put in the base test module 30 | single { JokeRepositoryImpl(get(), get()) } 31 | } 32 | ) 33 | ) { client -> 34 | // Setup 35 | val joke = transaction { 36 | Joke.new("joke_1") { 37 | this.value = "Chuck Norris tests are always green" 38 | this.createdAt = LocalDateTime.now() 39 | this.updatedAt = LocalDateTime.now() 40 | } 41 | } 42 | 43 | val response = client.get("/joke/random") 44 | assertEquals(HttpStatusCode.OK, response.status) 45 | 46 | val jokeDto = Json.decodeFromString(response.body()) 47 | 48 | assertEquals(transaction { joke.id.value }, jokeDto.jokeId) 49 | assertEquals(transaction { joke.value }, jokeDto.jokeContent) 50 | } 51 | } -------------------------------------------------------------------------------- /part3/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | val ktor_version: String by project 4 | val kotlin_version: String by project 5 | val logback_version: String by project 6 | val exposed_version: String by project 7 | val mysql_connector_version: String by project 8 | val hikaricp_version: String by project 9 | val h2_version: String by project 10 | val koin_version: String by project 11 | 12 | plugins { 13 | application 14 | kotlin("jvm") version "1.5.30" 15 | id("org.jetbrains.kotlin.plugin.serialization") version "1.5.30" 16 | } 17 | 18 | group = "com.prof18.ktor.chucknorris.sample" 19 | version = "0.0.1" 20 | application { 21 | mainClass.set("io.ktor.server.netty.EngineMain") 22 | } 23 | 24 | repositories { 25 | mavenCentral() 26 | } 27 | 28 | tasks.withType().all { 29 | kotlinOptions { 30 | jvmTarget = "11" 31 | } 32 | } 33 | 34 | dependencies { 35 | implementation("io.ktor:ktor-server-core:$ktor_version") 36 | implementation("io.ktor:ktor-serialization:$ktor_version") 37 | implementation("io.ktor:ktor-server-host-common:$ktor_version") 38 | implementation("io.ktor:ktor-server-netty:$ktor_version") 39 | implementation("io.ktor:ktor-locations:$ktor_version") 40 | implementation("ch.qos.logback:logback-classic:$logback_version") 41 | 42 | // Database 43 | implementation("org.jetbrains.exposed:exposed-core:$exposed_version") 44 | implementation("org.jetbrains.exposed:exposed-dao:$exposed_version") 45 | implementation("org.jetbrains.exposed:exposed-jdbc:$exposed_version") 46 | implementation("org.jetbrains.exposed:exposed-java-time:$exposed_version") 47 | implementation("mysql:mysql-connector-java:$mysql_connector_version") 48 | implementation("com.zaxxer:HikariCP:$hikaricp_version") 49 | 50 | // Koin 51 | implementation("io.insert-koin:koin-ktor:$koin_version") 52 | implementation("io.insert-koin:koin-logger-slf4j:$koin_version") 53 | 54 | // Testing 55 | testImplementation("io.ktor:ktor-server-tests:$ktor_version") 56 | testImplementation("com.h2database:h2:$h2_version") 57 | testImplementation("io.insert-koin:koin-test:$koin_version") 58 | testImplementation("io.insert-koin:koin-test-junit4:$koin_version") 59 | testImplementation("junit:junit:4.12") 60 | 61 | } -------------------------------------------------------------------------------- /part3/src/test/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/JokeRepositoryImplTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSource 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSourceImpl 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 6 | import com.prof18.ktor.chucknorris.sample.testutils.database.DatabaseFactoryForUnitTest 7 | import kotlinx.coroutines.runBlocking 8 | import org.jetbrains.exposed.sql.transactions.transaction 9 | import org.junit.After 10 | import org.junit.Assert.assertEquals 11 | import org.junit.Before 12 | import org.junit.Rule 13 | import org.junit.Test 14 | import org.koin.dsl.module 15 | import org.koin.test.KoinTest 16 | import org.koin.test.KoinTestRule 17 | import org.koin.test.inject 18 | import java.time.LocalDateTime 19 | 20 | class JokeRepositoryImplTest : KoinTest { 21 | 22 | private lateinit var databaseFactory: DatabaseFactoryForUnitTest 23 | 24 | @get:Rule 25 | val koinTestRule = KoinTestRule.create { 26 | // Your KoinApplication instance here 27 | modules(module { 28 | single { JokeLocalDataSourceImpl() } 29 | single { JokeRepositoryImpl(get()) } 30 | }) 31 | } 32 | 33 | private val jokeRepository: JokeRepository by inject() 34 | 35 | @Before 36 | fun setup() { 37 | databaseFactory = DatabaseFactoryForUnitTest() 38 | databaseFactory.connect() 39 | } 40 | 41 | @After 42 | fun tearDown() { 43 | databaseFactory.close() 44 | } 45 | 46 | @Test 47 | fun `getRandomJoke returns data correctly`() = runBlocking { 48 | // Setup 49 | val joke = transaction { 50 | Joke.new("joke_1") { 51 | this.value = "Chuck Norris tests are always green" 52 | this.createdAt = LocalDateTime.now() 53 | this.updatedAt = LocalDateTime.now() 54 | } 55 | } 56 | 57 | // Act 58 | val randomJoke = jokeRepository.getRandomJoke() 59 | 60 | // Assert 61 | assertEquals(transaction { joke.id.value }, randomJoke.jokeId) 62 | assertEquals(transaction { joke.value }, randomJoke.jokeContent) 63 | } 64 | } -------------------------------------------------------------------------------- /part4/src/test/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/JokeRepositoryImplTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSource 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSourceImpl 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 6 | import com.prof18.ktor.chucknorris.sample.testutils.database.DatabaseFactoryForUnitTest 7 | import kotlinx.coroutines.runBlocking 8 | import org.jetbrains.exposed.sql.transactions.transaction 9 | import org.junit.After 10 | import org.junit.Assert.assertEquals 11 | import org.junit.Before 12 | import org.junit.Rule 13 | import org.junit.Test 14 | import org.koin.dsl.module 15 | import org.koin.test.KoinTest 16 | import org.koin.test.KoinTestRule 17 | import org.koin.test.inject 18 | import java.time.LocalDateTime 19 | 20 | class JokeRepositoryImplTest : KoinTest { 21 | 22 | private lateinit var databaseFactory: DatabaseFactoryForUnitTest 23 | 24 | @get:Rule 25 | val koinTestRule = KoinTestRule.create { 26 | // Your KoinApplication instance here 27 | modules(module { 28 | single { JokeLocalDataSourceImpl() } 29 | single { JokeRepositoryImpl(get()) } 30 | }) 31 | } 32 | 33 | private val jokeRepository: JokeRepository by inject() 34 | 35 | @Before 36 | fun setup() { 37 | databaseFactory = DatabaseFactoryForUnitTest() 38 | databaseFactory.connect() 39 | } 40 | 41 | @After 42 | fun tearDown() { 43 | databaseFactory.close() 44 | } 45 | 46 | @Test 47 | fun `getRandomJoke returns data correctly`() = runBlocking { 48 | // Setup 49 | val joke = transaction { 50 | Joke.new("joke_1") { 51 | this.value = "Chuck Norris tests are always green" 52 | this.createdAt = LocalDateTime.now() 53 | this.updatedAt = LocalDateTime.now() 54 | } 55 | } 56 | 57 | // Act 58 | val randomJoke = jokeRepository.getRandomJoke() 59 | 60 | // Assert 61 | assertEquals(transaction { joke.id.value }, randomJoke.jokeId) 62 | assertEquals(transaction { joke.value }, randomJoke.jokeContent) 63 | } 64 | } -------------------------------------------------------------------------------- /part5/src/test/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/JokeRepositoryImplTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSource 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSourceImpl 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 6 | import com.prof18.ktor.chucknorris.sample.testutils.database.DatabaseFactoryForUnitTest 7 | import kotlinx.coroutines.runBlocking 8 | import org.jetbrains.exposed.sql.transactions.transaction 9 | import org.junit.After 10 | import org.junit.Assert.assertEquals 11 | import org.junit.Before 12 | import org.junit.Rule 13 | import org.junit.Test 14 | import org.koin.dsl.module 15 | import org.koin.test.KoinTest 16 | import org.koin.test.KoinTestRule 17 | import org.koin.test.inject 18 | import java.time.LocalDateTime 19 | 20 | class JokeRepositoryImplTest : KoinTest { 21 | 22 | private lateinit var databaseFactory: DatabaseFactoryForUnitTest 23 | 24 | @get:Rule 25 | val koinTestRule = KoinTestRule.create { 26 | // Your KoinApplication instance here 27 | modules(module { 28 | single { JokeLocalDataSourceImpl() } 29 | single { JokeRepositoryImpl(get()) } 30 | }) 31 | } 32 | 33 | private val jokeRepository: JokeRepository by inject() 34 | 35 | @Before 36 | fun setup() { 37 | databaseFactory = DatabaseFactoryForUnitTest() 38 | databaseFactory.connect() 39 | } 40 | 41 | @After 42 | fun tearDown() { 43 | databaseFactory.close() 44 | } 45 | 46 | @Test 47 | fun `getRandomJoke returns data correctly`() = runBlocking { 48 | // Setup 49 | val joke = transaction { 50 | Joke.new("joke_1") { 51 | this.value = "Chuck Norris tests are always green" 52 | this.createdAt = LocalDateTime.now() 53 | this.updatedAt = LocalDateTime.now() 54 | } 55 | } 56 | 57 | // Act 58 | val randomJoke = jokeRepository.getRandomJoke() 59 | 60 | // Assert 61 | assertEquals(transaction { joke.id.value }, randomJoke.jokeId) 62 | assertEquals(transaction { joke.value }, randomJoke.jokeContent) 63 | } 64 | } -------------------------------------------------------------------------------- /part3/src/main/kotlin/com/prof18/ktor/chucknorris/sample/Application.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample 2 | 3 | import ch.qos.logback.classic.Logger 4 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 5 | import com.prof18.ktor.chucknorris.sample.config.setupConfig 6 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 7 | import com.prof18.ktor.chucknorris.sample.di.appModule 8 | import com.prof18.ktor.chucknorris.sample.features.jokes.resource.jokeEndpoint 9 | import io.ktor.application.* 10 | import io.ktor.features.* 11 | import io.ktor.http.content.* 12 | import io.ktor.locations.* 13 | import io.ktor.response.* 14 | import io.ktor.routing.* 15 | import io.ktor.serialization.* 16 | import org.koin.core.module.Module 17 | import org.koin.ktor.ext.Koin 18 | import org.koin.ktor.ext.inject 19 | import org.koin.logger.slf4jLogger 20 | import org.slf4j.LoggerFactory 21 | import org.slf4j.event.Level 22 | // /Users/marco/Workspace/Examples/ktor-chuck-norris-sample 23 | // VM Options -DLOG_DEST=/Users/marco/Workspace/Examples/ktor-chuck-norris-sample/logs -DLOG_MAX_HISTORY=2 24 | fun main(args: Array): Unit = 25 | io.ktor.server.netty.EngineMain.main(args) 26 | 27 | /** 28 | * Please note that you can use any other name instead of *module*. 29 | * Also note that you can have more then one modules in your application. 30 | * */ 31 | @KtorExperimentalLocationsAPI 32 | @Suppress("unused") // Referenced in application.conf 33 | @kotlin.jvm.JvmOverloads 34 | fun Application.module(testing: Boolean = false, koinModules: List = listOf(appModule)) { 35 | 36 | install(Koin) { 37 | slf4jLogger() 38 | modules(koinModules) 39 | } 40 | 41 | setupConfig() 42 | 43 | val appConfig by inject() 44 | 45 | if (!appConfig.serverConfig.isProd) { 46 | val root = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME) as Logger 47 | root.level = ch.qos.logback.classic.Level.TRACE 48 | } 49 | 50 | val databaseFactory by inject() 51 | databaseFactory.connect() 52 | 53 | install(ContentNegotiation) { 54 | json() 55 | } 56 | 57 | install(CallLogging) { 58 | level = Level.INFO 59 | } 60 | 61 | install(Locations) 62 | 63 | routing { 64 | jokeEndpoint() 65 | get("/") { 66 | call.respondText("This is a sample Ktor backend to get Chuck Norris jokes") 67 | } 68 | } 69 | } 70 | 71 | 72 | -------------------------------------------------------------------------------- /part4/src/main/kotlin/com/prof18/ktor/chucknorris/sample/Application.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample 2 | 3 | import ch.qos.logback.classic.Logger 4 | import com.prof18.ktor.chucknorris.sample.config.AppConfig 5 | import com.prof18.ktor.chucknorris.sample.config.setupConfig 6 | import com.prof18.ktor.chucknorris.sample.database.DatabaseFactory 7 | import com.prof18.ktor.chucknorris.sample.di.appModule 8 | import com.prof18.ktor.chucknorris.sample.features.jokes.resource.jokeEndpoint 9 | import io.ktor.application.* 10 | import io.ktor.features.* 11 | import io.ktor.http.content.* 12 | import io.ktor.locations.* 13 | import io.ktor.response.* 14 | import io.ktor.routing.* 15 | import io.ktor.serialization.* 16 | import org.koin.core.module.Module 17 | import org.koin.ktor.ext.Koin 18 | import org.koin.ktor.ext.inject 19 | import org.koin.logger.slf4jLogger 20 | import org.slf4j.LoggerFactory 21 | import org.slf4j.event.Level 22 | // /Users/marco/Workspace/Examples/ktor-chuck-norris-sample 23 | // VM Options -DLOG_DEST=/Users/marco/Workspace/Examples/ktor-chuck-norris-sample/logs -DLOG_MAX_HISTORY=2 24 | fun main(args: Array): Unit = 25 | io.ktor.server.netty.EngineMain.main(args) 26 | 27 | /** 28 | * Please note that you can use any other name instead of *module*. 29 | * Also note that you can have more then one modules in your application. 30 | * */ 31 | @KtorExperimentalLocationsAPI 32 | @Suppress("unused") // Referenced in application.conf 33 | @kotlin.jvm.JvmOverloads 34 | fun Application.module(testing: Boolean = false, koinModules: List = listOf(appModule)) { 35 | 36 | install(Koin) { 37 | slf4jLogger() 38 | modules(koinModules) 39 | } 40 | 41 | setupConfig() 42 | 43 | val appConfig by inject() 44 | 45 | if (!appConfig.serverConfig.isProd) { 46 | val root = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME) as Logger 47 | root.level = ch.qos.logback.classic.Level.TRACE 48 | } 49 | 50 | val databaseFactory by inject() 51 | databaseFactory.connect() 52 | 53 | install(ContentNegotiation) { 54 | json() 55 | } 56 | 57 | install(CallLogging) { 58 | level = Level.INFO 59 | } 60 | 61 | install(Locations) 62 | 63 | routing { 64 | jokeEndpoint() 65 | get("/") { 66 | call.respondText("This is a sample Ktor backend to get Chuck Norris jokes") 67 | } 68 | } 69 | } 70 | 71 | 72 | -------------------------------------------------------------------------------- /part3/src/test/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/resource/JokeResourceTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.resource 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepositoryImpl 6 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 7 | import com.prof18.ktor.chucknorris.sample.testutils.appTestModule 8 | import com.prof18.ktor.chucknorris.sample.testutils.withTestServer 9 | import io.ktor.http.* 10 | import io.ktor.locations.* 11 | import io.ktor.server.testing.* 12 | import kotlinx.serialization.ExperimentalSerializationApi 13 | import kotlinx.serialization.decodeFromString 14 | import kotlinx.serialization.json.Json 15 | import org.jetbrains.exposed.sql.transactions.transaction 16 | import org.junit.Assert.assertEquals 17 | import org.junit.Test 18 | import org.koin.dsl.module 19 | import org.koin.test.AutoCloseKoinTest 20 | import java.time.LocalDateTime 21 | 22 | @KtorExperimentalLocationsAPI 23 | @ExperimentalSerializationApi 24 | class JokeResourceTest : AutoCloseKoinTest() { 25 | 26 | @Test 27 | fun `random joke api works correctly`() = withTestServer( 28 | koinModules = appTestModule.plus( 29 | module { 30 | // Just to showcase the possibility, in this case this dependency can be put in the base test module 31 | single { JokeRepositoryImpl(get()) } 32 | } 33 | ) 34 | ) { 35 | 36 | // Setup 37 | val joke = transaction { 38 | Joke.new("joke_1") { 39 | this.value = "Chuck Norris tests are always green" 40 | this.createdAt = LocalDateTime.now() 41 | this.updatedAt = LocalDateTime.now() 42 | } 43 | } 44 | 45 | val href = application.locations.href( 46 | JokeEndpoint.Random( 47 | parent = JokeEndpoint() 48 | ) 49 | ) 50 | 51 | handleRequest(HttpMethod.Get, href).apply { 52 | assertEquals(HttpStatusCode.OK, response.status()) 53 | 54 | val response = Json.decodeFromString(response.content!!) 55 | 56 | assertEquals(transaction { joke.id.value }, response.jokeId) 57 | assertEquals(transaction { joke.value }, response.jokeContent) 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /part4/src/test/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/resource/JokeResourceTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.resource 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepositoryImpl 6 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 7 | import com.prof18.ktor.chucknorris.sample.testutils.appTestModule 8 | import com.prof18.ktor.chucknorris.sample.testutils.withTestServer 9 | import io.ktor.http.* 10 | import io.ktor.locations.* 11 | import io.ktor.server.testing.* 12 | import kotlinx.serialization.ExperimentalSerializationApi 13 | import kotlinx.serialization.decodeFromString 14 | import kotlinx.serialization.json.Json 15 | import org.jetbrains.exposed.sql.transactions.transaction 16 | import org.junit.Assert.assertEquals 17 | import org.junit.Test 18 | import org.koin.dsl.module 19 | import org.koin.test.AutoCloseKoinTest 20 | import java.time.LocalDateTime 21 | 22 | @KtorExperimentalLocationsAPI 23 | @ExperimentalSerializationApi 24 | class JokeResourceTest : AutoCloseKoinTest() { 25 | 26 | @Test 27 | fun `random joke api works correctly`() = withTestServer( 28 | koinModules = appTestModule.plus( 29 | module { 30 | // Just to showcase the possibility, in this case this dependency can be put in the base test module 31 | single { JokeRepositoryImpl(get()) } 32 | } 33 | ) 34 | ) { 35 | 36 | // Setup 37 | val joke = transaction { 38 | Joke.new("joke_1") { 39 | this.value = "Chuck Norris tests are always green" 40 | this.createdAt = LocalDateTime.now() 41 | this.updatedAt = LocalDateTime.now() 42 | } 43 | } 44 | 45 | val href = application.locations.href( 46 | JokeEndpoint.Random( 47 | parent = JokeEndpoint() 48 | ) 49 | ) 50 | 51 | handleRequest(HttpMethod.Get, href).apply { 52 | assertEquals(HttpStatusCode.OK, response.status()) 53 | 54 | val response = Json.decodeFromString(response.content!!) 55 | 56 | assertEquals(transaction { joke.id.value }, response.jokeId) 57 | assertEquals(transaction { joke.value }, response.jokeContent) 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /part5/src/test/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/resource/JokeResourceTest.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.resource 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.dao.Joke 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepository 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.JokeRepositoryImpl 6 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 7 | import com.prof18.ktor.chucknorris.sample.testutils.appTestModule 8 | import com.prof18.ktor.chucknorris.sample.testutils.withTestServer 9 | import io.ktor.http.* 10 | import io.ktor.locations.* 11 | import io.ktor.server.testing.* 12 | import kotlinx.serialization.ExperimentalSerializationApi 13 | import kotlinx.serialization.decodeFromString 14 | import kotlinx.serialization.json.Json 15 | import org.jetbrains.exposed.sql.transactions.transaction 16 | import org.junit.Assert.assertEquals 17 | import org.junit.Test 18 | import org.koin.dsl.module 19 | import org.koin.test.AutoCloseKoinTest 20 | import java.time.LocalDateTime 21 | 22 | @ExperimentalSerializationApi 23 | @KtorExperimentalLocationsAPI 24 | class JokeResourceTest : AutoCloseKoinTest() { 25 | 26 | @Test 27 | fun `random joke api works correctly`() = withTestServer( 28 | koinModules = appTestModule.plus( 29 | module { 30 | // Just to showcase the possibility, in this case this dependency can be put in the base test module 31 | single { JokeRepositoryImpl(get()) } 32 | } 33 | ) 34 | ) { 35 | 36 | // Setup 37 | val joke = transaction { 38 | Joke.new("joke_1") { 39 | this.value = "Chuck Norris tests are always green" 40 | this.createdAt = LocalDateTime.now() 41 | this.updatedAt = LocalDateTime.now() 42 | } 43 | } 44 | 45 | val href = application.locations.href( 46 | JokeEndpoint.Random( 47 | parent = JokeEndpoint() 48 | ) 49 | ) 50 | 51 | handleRequest(HttpMethod.Get, href).apply { 52 | assertEquals(HttpStatusCode.OK, response.status()) 53 | 54 | val response = Json.decodeFromString(response.content!!) 55 | 56 | assertEquals(transaction { joke.id.value }, response.jokeId) 57 | assertEquals(transaction { joke.value }, response.jokeContent) 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /part6/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/JokeRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSource 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.mapper.toDTO 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 6 | import com.prof18.ktor.chucknorris.sample.jobs.JobSchedulerManager 7 | import com.prof18.ktor.chucknorris.sample.jobs.RandomJokeJob 8 | import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction 9 | import org.quartz.* 10 | 11 | class JokeRepositoryImpl( 12 | private val jokeLocalDataSource: JokeLocalDataSource, 13 | private val jobSchedulerManager: JobSchedulerManager 14 | ) : JokeRepository { 15 | 16 | override suspend fun getRandomJoke(): JokeDTO { 17 | val jokeDTO = newSuspendedTransaction { 18 | val allJokes = jokeLocalDataSource.getAllJokes() 19 | val randomJoke = allJokes.random() 20 | return@newSuspendedTransaction randomJoke.toDTO() 21 | } 22 | return jokeDTO 23 | } 24 | 25 | override suspend fun watch(name: String) { 26 | val jobId = "chuck-watch-job-for-name-$name" 27 | val triggerId = "chuck-watch-trigger-for-name-$name" 28 | 29 | // If a job exists, delete it! 30 | val jobScheduler = jobSchedulerManager.scheduler 31 | val jobKey = JobKey.jobKey(jobId, RandomJokeJob.WATCH_JOB_GROUP) 32 | jobScheduler.deleteJob(jobKey) 33 | 34 | val job: JobDetail = JobBuilder.newJob(RandomJokeJob::class.java) 35 | .withIdentity(jobId, RandomJokeJob.WATCH_JOB_GROUP) 36 | .usingJobData(RandomJokeJob.JOB_MAP_NAME_ID_KEY, name) 37 | .build() 38 | 39 | val trigger: Trigger = TriggerBuilder.newTrigger() 40 | .withIdentity(triggerId, RandomJokeJob.WATCH_JOB_GROUP) 41 | .withSchedule( 42 | SimpleScheduleBuilder.simpleSchedule() 43 | // every minute 44 | .withIntervalInMinutes(1) 45 | .repeatForever() 46 | ) 47 | .build() 48 | 49 | // Tell quartz to schedule the job using our trigger 50 | jobSchedulerManager.scheduler.scheduleJob(job, trigger) 51 | } 52 | 53 | override fun getChuckGreeting(name: String): String { 54 | return "Hi $name, remember that Chuck Norris is watching you 👀" 55 | } 56 | } -------------------------------------------------------------------------------- /ktor-1.6.x/src/main/kotlin/com/prof18/ktor/chucknorris/sample/features/jokes/domain/JokeRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.prof18.ktor.chucknorris.sample.features.jokes.domain 2 | 3 | import com.prof18.ktor.chucknorris.sample.features.jokes.data.JokeLocalDataSource 4 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.mapper.toDTO 5 | import com.prof18.ktor.chucknorris.sample.features.jokes.domain.model.JokeDTO 6 | import com.prof18.ktor.chucknorris.sample.jobs.JobSchedulerManager 7 | import com.prof18.ktor.chucknorris.sample.jobs.RandomJokeJob 8 | import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction 9 | import org.quartz.* 10 | 11 | class JokeRepositoryImpl( 12 | private val jokeLocalDataSource: JokeLocalDataSource, 13 | private val jobSchedulerManager: JobSchedulerManager 14 | ) : JokeRepository { 15 | 16 | override suspend fun getRandomJoke(): JokeDTO { 17 | val jokeDTO = newSuspendedTransaction { 18 | val allJokes = jokeLocalDataSource.getAllJokes() 19 | val randomJoke = allJokes.random() 20 | return@newSuspendedTransaction randomJoke.toDTO() 21 | } 22 | return jokeDTO 23 | } 24 | 25 | override suspend fun watch(name: String) { 26 | val jobId = "chuck-watch-job-for-name-$name" 27 | val triggerId = "chuck-watch-trigger-for-name-$name" 28 | 29 | // If a job exists, delete it! 30 | val jobScheduler = jobSchedulerManager.scheduler 31 | val jobKey = JobKey.jobKey(jobId, RandomJokeJob.WATCH_JOB_GROUP) 32 | jobScheduler.deleteJob(jobKey) 33 | 34 | val job: JobDetail = JobBuilder.newJob(RandomJokeJob::class.java) 35 | .withIdentity(jobId, RandomJokeJob.WATCH_JOB_GROUP) 36 | .usingJobData(RandomJokeJob.JOB_MAP_NAME_ID_KEY, name) 37 | .build() 38 | 39 | val trigger: Trigger = TriggerBuilder.newTrigger() 40 | .withIdentity(triggerId, RandomJokeJob.WATCH_JOB_GROUP) 41 | .withSchedule( 42 | SimpleScheduleBuilder.simpleSchedule() 43 | // every minute 44 | .withIntervalInMinutes(1) 45 | .repeatForever() 46 | ) 47 | .build() 48 | 49 | // Tell quartz to schedule the job using our trigger 50 | jobSchedulerManager.scheduler.scheduleJob(job, trigger) 51 | } 52 | 53 | override fun getChuckGreeting(name: String): String { 54 | return "Hi $name, remember that Chuck Norris is watching you 👀" 55 | } 56 | } --------------------------------------------------------------------------------