Skip to content

Commit

Permalink
Introduce a ktor feature #37
Browse files Browse the repository at this point in the history
  • Loading branch information
Allali84 authored and jeggy committed Mar 1, 2020
1 parent 333f42d commit 195e35a
Show file tree
Hide file tree
Showing 22 changed files with 10,036 additions and 6 deletions.
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ plugins {
id "org.jetbrains.kotlin.jvm" version "1.3.61"
id "com.github.ben-manes.versions" version "0.24.0"
id "com.jfrog.bintray" version "1.8.4"
id 'org.jetbrains.kotlin.plugin.serialization' version '1.3.61'
id "maven-publish"
id "jacoco"
}
Expand All @@ -16,6 +17,7 @@ allprojects {

repositories {
jcenter()
maven { url 'https://jitpack.io' }
}

dependencies {
Expand Down
12 changes: 12 additions & 0 deletions example/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apply plugin: 'application'
mainClassName = "io.ktor.server.netty.EngineMain"
dependencies {
compile project(':kgraphql-ktor')
compile "io.ktor:ktor-server-netty:$ktor_version"
compile "ch.qos.logback:logback-classic:$logback_version"
compile "org.jetbrains.exposed:exposed-core:$exposed_version"
compile "org.jetbrains.exposed:exposed-jdbc:$exposed_version"
compile "org.jetbrains.exposed:exposed-java-time:$exposed_version"
compile "com.h2database:h2:$h2_version"
compile "com.zaxxer:HikariCP:$hikari_version"
}
5 changes: 5 additions & 0 deletions example/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Dependencies
logback_version=1.2.1
exposed_version=0.21.1
h2_version=1.4.200
hikari_version=3.4.2
125 changes: 125 additions & 0 deletions example/src/main/kotlin/com/apurebase/kgraphql/Application.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.apurebase.kgraphql

import com.apurebase.kgraphql.dao.DatabaseFactory
import com.apurebase.kgraphql.exception.NotFoundException
import com.apurebase.kgraphql.model.*
import com.apurebase.kgraphql.service.UFOSightingService
import io.ktor.application.Application
import io.ktor.application.install
import io.ktor.features.CORS
import io.ktor.features.DefaultHeaders
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.routing.routing
import io.ktor.util.KtorExperimentalAPI
import kotlinx.serialization.UnstableDefault
import java.time.LocalDate

fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)


@UnstableDefault
@KtorExperimentalAPI
fun Application.module() {
install(DefaultHeaders)
install(CORS) {
method(HttpMethod.Post)
header(HttpHeaders.AccessControlAllowHeaders)
header(HttpHeaders.ContentType)
header(HttpHeaders.AccessControlAllowOrigin)
anyHost()
maxAgeInSeconds = CORS.CORS_DEFAULT_MAX_AGE
}
DatabaseFactory.init()
val service = UFOSightingService()
routing {
graphql {

configure {
useDefaultPrettyPrinter = true
}

stringScalar<LocalDate> {
serialize = { date -> date.toString() }
deserialize = { dateString -> LocalDate.parse(dateString) }
}

query("sightings") {
description = "Returns a subset of the UFO Sighting records"

resolver { size: Int? -> service.findAll(size ?: 10).toMutableList() }.withArgs {
arg<Int> { name = "size"; defaultValue = 10; description = "The number of records to return" }
}
}

query("sighting") {
description = "Returns a single UFO Sighting record based on the id"

resolver { id: Int ->
service.findById(id) ?: throw NotFoundException("Sighting with id: $id does not exist")
}
}

query("user") {
description = "Returns a single User based on the id"

resolver { id: Int ->
users.getOrNull(id - 1) ?: throw NotFoundException("User with id: $id does not exist")
}
}

type<User> {
description = "A User who has reported a UFO sighting"

property<UFOSighting?>("sighting") {
resolver { user -> service.findById(user.id) }
}
}

mutation("createUFOSighting") {
description = "Adds a new UFO Sighting to the database"

resolver {
input: CreateUFOSightingInput -> service.create(input.toUFOSighting())
}

}

query("topSightings") {
description = "Returns a list of the top state,country based on the number of sightings"

resolver { -> service.getTopSightings() }
}

query("topCountrySightings") {
description = "Returns a list of the top countries based on the number of sightings"

resolver { -> service.getTopCountrySightings() }
}

type<CountrySightings> {
description = "A country sighting; contains total number of occurrences"

property(CountrySightings::numOccurrences) {
description = "The number of occurrences of the sighting"
}
}

inputType<CreateUFOSightingInput>()

type<UFOSighting> {
description = "A UFO sighting"

property(UFOSighting::dateSighting) {
description = "The date of the sighting"
}

property<User>("user") {
resolver {
users[(0..2).shuffled().last()]
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.apurebase.kgraphql

import com.apurebase.kgraphql.model.UFOSighting
import java.time.LocalDate

fun CreateUFOSightingInput.toUFOSighting() : UFOSighting {
return UFOSighting(
dateSighting = this.date,
city = this.country[0].city,
state = this.country[0].state,
country = this.country[0].country,
shape = this.shape,
duration = this.duration ?: 0.0,
comments = this.country[0].comments,
latitude = this.latitude ?: 0.0,
longitude = this.longitude ?: 0.0
)
}

data class CreateUFOSightingInput(
var date: LocalDate = LocalDate.now(),
var country: List<CountryInput> = emptyList(),
var shape: String = "",
var duration: Double = 0.0,
var latitude: Double = 0.0,
var longitude: Double = 0.0
)


data class CountryInput(
var city: String = "",
var state: String = "",
var country: String = "",
var comments: String = ""
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.apurebase.kgraphql.dao

import java.io.BufferedReader
import java.io.InputStream
import java.io.InputStreamReader

object CSVDataImporter {
fun importFromCsv(inputStream: InputStream?, consumer: (Array<String>)-> Unit) {
inputStream ?: throw RuntimeException("Failed to perform import")
val bufferedReader = BufferedReader(InputStreamReader(inputStream))

try {
return bufferedReader.useLines { lines ->
lines.drop(1).forEach { line ->
consumer(line.split("\\s*,\\s*".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray())
}
}
} catch (e: Exception) {
println(e)
throw RuntimeException("Failed to perform import", e)
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.apurebase.kgraphql.dao

import com.apurebase.kgraphql.dao.CSVDataImporter.importFromCsv
import com.apurebase.kgraphql.model.UFOSightings
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils.create
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
import org.jetbrains.exposed.sql.transactions.transaction
import java.io.InputStream
import java.sql.ResultSet
import java.time.LocalDate
import java.time.format.DateTimeFormatter

object DatabaseFactory {

private fun Thread.asResourceStream(): InputStream? = this.contextClassLoader.getResourceAsStream(CSV_FILE_NAME)

private const val CSV_FILE_NAME = "ufo_sightings_2013_2014"
private const val DATE_FORMAT = "M/d/yyyy"

fun init() {
Database.connect(hikari())
transaction {
create(UFOSightings)
val formatter = DateTimeFormatter.ofPattern(DATE_FORMAT)
val settingsStream = Thread.currentThread().asResourceStream()
importFromCsv(settingsStream) { row ->
UFOSightings.insert {
it[dateSighting] = LocalDate.parse(row[0], formatter)
it[city] = row[1]
it[state] = row[2]
it[country] = row[3]
it[shape] = row[4]
it[duration] = row[5].toDouble()
it[comments] = row[6]
it[latitude] = row[7].toDouble()
it[longitude] = row[8].toDouble()
}
}
}
}

private fun hikari(): HikariDataSource {
val config = HikariConfig()
config.driverClassName = "org.h2.Driver"
config.jdbcUrl = "jdbc:h2:mem:test"
config.maximumPoolSize = 3
config.isAutoCommit = false
config.transactionIsolation = "TRANSACTION_REPEATABLE_READ"
config.validate()
return HikariDataSource(config)
}

suspend fun <T> dbQuery(
block: suspend () -> T): T =
newSuspendedTransaction { block() }


}



fun <T:Any> String.execAndMap(transform : (ResultSet) -> T) : MutableList<T> {
val result = arrayListOf<T>()
TransactionManager.current().exec(this) { rs ->
while (rs.next()) {
result += transform(rs)
}
}
return result
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.apurebase.kgraphql.exception

class NotFoundException(message : String) : RuntimeException(message)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.apurebase.kgraphql.model


data class CountrySightings(
var state: String = "",
var country: String = "",
var numOccurrences: Int = 0
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.apurebase.kgraphql.model

import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.`java-time`.date
import java.time.LocalDate

object UFOSightings : Table() {
val id = integer("id").autoIncrement()
val dateSighting = date("seeing_at")
val city = varchar("city", 250)
val state = varchar("state", 250)
val country = varchar("country", 250)
val shape = varchar("shape", 250)
val duration = double("duration")
val comments = varchar("comments", 250)
val latitude = double("latitude")
val longitude = double("longitude")

override val primaryKey = PrimaryKey(id, name = "PK_UFOSighting_ID")
val uniqueIndex = uniqueIndex("IDX_UFOSighting_UNIQUE", dateSighting, city, state, country, shape, duration, comments)

}

data class UFOSighting (
var id: Int = -1,
var dateSighting: LocalDate = LocalDate.now(),
var city: String = "",
var state: String = "",
var country: String = "",
var shape: String = "",
var duration: Double = 0.0,
var comments: String = "",
var latitude: Double = 0.0,
var longitude: Double = 0.0
)

8 changes: 8 additions & 0 deletions example/src/main/kotlin/com/apurebase/kgraphql/model/User.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.apurebase.kgraphql.model

val users = listOf(
User(id = 1, name = "Amber"),
User(id = 2, name = "Greg"),
User(id = 3, name = "Frank"))

data class User(val id: Int = -1, val name: String = "")
Loading

0 comments on commit 195e35a

Please sign in to comment.