Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate data on server using schema #100

Merged
merged 2 commits into from
Jul 5, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions data.schema.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://atlas.cuhacking.com/data.schema.json",
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://atlas.cuhacking.com/schema/0.1/data.schema.json",
"title": "Atlas Map Data Schema Definition",
"description": "Data used in project Atlas",
"type": "object",
Expand Down
5 changes: 3 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ ios = "14.0"

detekt = "1.16.0"
kotlin = "1.5.10"
ktor = "1.6.0"
ktor = "1.6.1"
spatialk = "0.1.1"
sqldelight = "1.6.0-SNAPSHOT"
coroutines = "1.5.0-native-mt"
Expand Down Expand Up @@ -66,8 +66,9 @@ mapbox-android = "com.mapbox.mapboxsdk:mapbox-android-sdk:9.6.1"
logback = "ch.qos.logback:logback-classic:1.2.3"
material = "com.google.android.material:material:1.3.0-beta01"
turbine = "app.cash.turbine:turbine:0.3.0"
clikt = "com.github.ajalt.clikt:clikt:3.0.1"
clikt = "com.github.ajalt.clikt:clikt:3.2.0"
klock = "com.soywiz.korlibs.klock:klock:2.1.2"
json-schema = "com.github.everit-org.json-schema:org.everit.json.schema:1.12.3"

[bundles]
androidx-runtime = ["androidx-core", "androidx-appCompat", "androidx-constraintLayout", "androidx-lifecycle"]
Expand Down
5 changes: 5 additions & 0 deletions server/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@ plugins {
id("com.github.johnrengelman.shadow")
}

repositories {
maven(url = "https://jitpack.io")
}

kotlin {
dependencies {
implementation(libs.ktor.server.core)
implementation(libs.ktor.server.cio)
implementation(libs.logback)
implementation(libs.clikt)
implementation(libs.spatialk.geojson)
implementation(libs.json.schema)

testImplementation(libs.ktor.server.test)
testImplementation(libs.junit)
Expand Down
53 changes: 43 additions & 10 deletions server/src/main/kotlin/com/cuhacking/atlas/server/Server.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ package com.cuhacking.atlas.server
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.types.int
import com.github.ajalt.clikt.parameters.types.path
import io.github.dellisd.spatialk.geojson.FeatureCollection.Companion.toFeatureCollection
import io.ktor.application.*
import io.ktor.features.*
import io.ktor.server.engine.*
import io.ktor.server.cio.*
import io.ktor.server.engine.*
import org.everit.json.schema.ValidationException
import org.everit.json.schema.loader.SchemaLoader
import org.json.JSONObject
import org.json.JSONTokener
import kotlin.io.path.readText

class Server : CliktCommand() {
private val file by argument(help = "Path to directory containing data files").path(
Expand All @@ -21,18 +26,46 @@ class Server : CliktCommand() {
@Suppress("MagicNumber")
val port by option("--port", help = "Port for the server to listen on").int().default(8080)

private val test by option(
"--test",
"-t",
help = "Validate data against the schema without starting the server"
).flag()

override fun run() {
verifyData()
if (!verifyData()) {
throw RuntimeException()
}

embeddedServer(CIO, port, module = {
install(AutoHeadResponse)
dataModuleFactory(file)
}).start(wait = true)
if (!test) {
embeddedServer(CIO, port, module = {
install(AutoHeadResponse)
install(CallLogging)
dataModuleFactory(file)
}).start(wait = true)
}
}

internal fun verifyData() {
// Will throw error if file is not a valid FeatureCollection
file.toFile().readText().toFeatureCollection()
private fun verifyData(): Boolean {
var valid = true

val stream = javaClass.classLoader.getResourceAsStream("data.schema.json")
val rawSchema = JSONObject(JSONTokener(stream))
val schema = SchemaLoader.load(rawSchema)

val json = file.readText()

try {
schema.validate(JSONObject(json))
} catch (e: ValidationException) {
valid = false
println(e.message)
e.causingExceptions.map(ValidationException::message).forEach(::println)
} catch (e: Exception) {
println(e)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any other particular errors we're expecting this to throw? Will returning true in any of those cases cause issues?

}

return valid
}
}

Expand Down
Loading