-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathHttpRoutingExample.kt
149 lines (120 loc) · 4.45 KB
/
HttpRoutingExample.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package com.natpryce.krouton.example
import com.natpryce.hamkrest.assertion.assertThat
import com.natpryce.hamkrest.equalTo
import com.natpryce.hamkrest.isEmptyString
import com.natpryce.hamkrest.present
import com.natpryce.krouton.PathTemplate
import com.natpryce.krouton.PathTemplate2
import com.natpryce.krouton.Projection
import com.natpryce.krouton.Tuple3
import com.natpryce.krouton.asA
import com.natpryce.krouton.getValue
import com.natpryce.krouton.http4k.resources
import com.natpryce.krouton.int
import com.natpryce.krouton.locale
import com.natpryce.krouton.path
import com.natpryce.krouton.plus
import com.natpryce.krouton.root
import com.natpryce.krouton.string
import com.natpryce.krouton.tuple
import dev.minutest.rootContext
import org.http4k.core.HttpHandler
import org.http4k.core.Method.GET
import org.http4k.core.Response
import org.http4k.core.Status
import org.http4k.core.Status.Companion.FOUND
import org.http4k.core.Status.Companion.MOVED_PERMANENTLY
import org.http4k.core.Status.Companion.NOT_FOUND
import org.http4k.core.Status.Companion.OK
import org.junit.platform.commons.annotation.Testable
import java.time.DateTimeException
import java.time.LocalDate
import java.time.LocalDate.now
import java.time.format.DateTimeFormatter
import java.util.Locale
// An application-specific mapping between parsed URL elements and typed data
object LocalDate_ : Projection<Tuple3<Int, Int, Int>, LocalDate> {
override fun fromParts(parts: Tuple3<Int, Int, Int>): LocalDate? {
val (year, month, day) = parts
return try {
LocalDate.of(year, month, day)
}
catch (e: DateTimeException) {
null
}
}
override fun toParts(mapped: LocalDate) =
tuple(mapped.year, mapped.monthValue, mapped.dayOfMonth)
}
// Components of the application's routes
val year by int
val month by int
val day by int
val date = year + month + day asA LocalDate_
// The application's routes
val reverse = root + "reverse" + string
val negate = root + "negate" + int
// Note: without these explicit type declarations, the Kotlin compiler crashes with an internal error
val weekday: PathTemplate2<Locale, LocalDate> = root + "weekday" + locale.named("locale") + date
val weekdayToday: PathTemplate<Locale> = root + "weekday" + locale.named("locale") + "today"
// Obsolete routes that each redirect to one of the routes above
val negative = root + "negative" + int
val reversed = root + "reversed" + string
// The server that uses the routes
val demo = resources {
root methods {
GET { ok("Hello, World.") }
}
negate methods {
GET { _, i -> ok((-i).toString()) }
}
negative methods {
// Note - reverse routing from integer to URL path
GET { _, i -> redirect(MOVED_PERMANENTLY, negate.path(i)) }
}
reverse methods {
GET { _, s -> ok(s.reversed()) }
}
reversed methods {
GET { _, s -> redirect(MOVED_PERMANENTLY, reverse.path(s)) }
}
weekday methods {
GET { _, (locale, date) -> ok(date.format(DateTimeFormatter.ofPattern("EEEE", locale))) }
}
weekdayToday methods {
/* Note - reverse routing using user-defined projection*/
GET { _, locale -> redirect(FOUND, weekday.path(locale, now())) }
}
}
private fun ok(s: String) =
Response(OK).body(s)
private fun redirect(status: Status, location: String) =
Response(status).header("Location", location)
@Testable
fun `HttpRouting tests`() = rootContext<HttpHandler> {
fixture { demo }
test("negate") {
assertThat(getText("/negate/100"), equalTo("-100"))
}
test("negative_redirects_to_negate") {
assertThat(getText("/negative/20"), equalTo("-20"))
}
test("reverse") {
assertThat(getText("/reverse/hello%20world"), equalTo("dlrow olleh"))
}
test("weekday") {
assertThat(getText("/weekday/en/2016/02/29"), equalTo("Monday"))
assertThat(getText("/weekday/fr/2016/02/29"), equalTo("lundi"))
assertThat(getText("/weekday/de/2016/02/29"), equalTo("Montag"))
assertThat(getText("/weekday/en/2016/03/01"), equalTo("Tuesday"))
}
test("weekday_today") {
assertThat(getText("/weekday/en/today"), present(!isEmptyString))
}
test("bad_dates_not_found") {
assertThat(get("/weekday/2016/02/30").status, equalTo(NOT_FOUND))
}
test("root") {
assertThat(getText("/"), equalTo("Hello, World."))
}
}