Skip to content

Commit cb7599b

Browse files
author
Robert Winkler
committed
Added WoT schemas
1 parent 7dffca5 commit cb7599b

39 files changed

+893
-582
lines changed

kotlin-wot-binding-http/src/main/kotlin/http/HttpProtocolClient.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ class HttpProtocolClient(
108108
return when (response.status.value) {
109109
in HttpStatusCode.OK.value..<HttpStatusCode.MultipleChoices.value -> {
110110
val body = response.readRawBytes()
111-
Content(response.contentType().toString(), body)
111+
val contentType = response.contentType() ?: ContentType.Application.Json
112+
Content(contentType.toString(), body)
112113
}
113114
in HttpStatusCode.MultipleChoices.value..<HttpStatusCode.BadRequest.value -> {
114115
throw ProtocolClientException("Received ${response.status.value} and cannot continue (not implemented)")

kotlin-wot-binding-http/src/main/kotlin/http/HttpProtocolServer.kt

Lines changed: 23 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import ai.ancf.lmos.wot.Servient
44
import ai.ancf.lmos.wot.content.Content
55
import ai.ancf.lmos.wot.content.ContentCodecException
66
import ai.ancf.lmos.wot.content.ContentManager
7-
import ai.ancf.lmos.wot.thing.ExposedThingImpl
87
import ai.ancf.lmos.wot.thing.form.Form
98
import ai.ancf.lmos.wot.thing.form.Operation
10-
import ai.ancf.lmos.wot.thing.property.ExposedThingProperty.*
9+
import ai.ancf.lmos.wot.thing.schema.ExposedThing
10+
import ai.ancf.lmos.wot.thing.schema.InteractionAffordance
1111
import ai.anfc.lmos.wot.binding.ProtocolServer
1212
import ai.anfc.lmos.wot.binding.ProtocolServerException
1313
import com.fasterxml.jackson.databind.DeserializationFeature
@@ -34,7 +34,7 @@ open class HttpProtocolServer(
3434
private val bindPort: Int = 8080,
3535
private val createServer: (host: String, port: Int, servient: Servient) -> EmbeddedServer<*, *> = ::defaultServer
3636
) : ProtocolServer {
37-
val things: MutableMap<String, ExposedThingImpl> = mutableMapOf()
37+
val things: MutableMap<String, ExposedThing> = mutableMapOf()
3838
var started = false
3939
private var server: EmbeddedServer<*, *>? = null
4040
private var actualAddresses: List<String> = listOf("http://$bindHost:$bindPort")
@@ -59,7 +59,7 @@ open class HttpProtocolServer(
5959
}
6060

6161
// Expose a thing
62-
override fun expose(thing: ExposedThingImpl) {
62+
override fun expose(thing: ExposedThing) {
6363
if (!started) throw ProtocolServerException("Server has not started yet")
6464

6565
log.info("Exposing thing '{}'", thing.id)
@@ -82,7 +82,7 @@ open class HttpProtocolServer(
8282
}
8383
}
8484

85-
internal fun exposeProperties(thing: ExposedThingImpl, address: String, contentType: String) {
85+
internal fun exposeProperties(thing: ExposedThing, address: String, contentType: String) {
8686
thing.properties.forEach { (name, property) ->
8787

8888
val href = getHrefWithVariablePattern(address, thing, "properties", name, property)
@@ -128,7 +128,7 @@ open class HttpProtocolServer(
128128
}
129129
}
130130

131-
internal fun exposeActions(thing: ExposedThingImpl, address: String, contentType: String) {
131+
internal fun exposeActions(thing: ExposedThing, address: String, contentType: String) {
132132
thing.actions.forEach { (name, action) ->
133133
val href: String = getHrefWithVariablePattern(address, thing, "actions", name, action)
134134
// Initialize the form using named parameters
@@ -144,7 +144,7 @@ open class HttpProtocolServer(
144144
}
145145
}
146146

147-
internal fun exposeEvents(thing: ExposedThingImpl, address: String, contentType: String) {
147+
internal fun exposeEvents(thing: ExposedThing, address: String, contentType: String) {
148148
thing.events.forEach { (name, event) ->
149149
val href = getHrefWithVariablePattern(address, thing, "events", name, event)
150150

@@ -164,7 +164,7 @@ open class HttpProtocolServer(
164164

165165
private fun getHrefWithVariablePattern(
166166
address: String,
167-
thing: ExposedThingImpl,
167+
thing: ExposedThing,
168168
type: String,
169169
interactionName: String,
170170
interaction: InteractionAffordance
@@ -178,7 +178,7 @@ open class HttpProtocolServer(
178178
}
179179

180180
// Destroy a thing
181-
override suspend fun destroy(thing: ExposedThingImpl) {
181+
override suspend fun destroy(thing: ExposedThing) {
182182
log.info("Removing thing '{}'", thing.id)
183183
things.remove(thing.id)
184184
}
@@ -197,15 +197,15 @@ fun Application.setupRouting(servient: Servient) {
197197
routing {
198198
route("/") {
199199
get {
200-
call.respond(servient.things.values.toList(), typeInfo<List<ExposedThingImpl>>())
200+
call.respond(servient.things.values.toList(), typeInfo<List<ExposedThing>>())
201201
}
202202
}
203203
route("/{id}") {
204204
get {
205205
val id = call.parameters["id"]
206-
val thing: ExposedThingImpl? = servient.things[id]
206+
val thing: ExposedThing? = servient.things[id]
207207
if (thing != null) {
208-
call.respond(thing, typeInfo<ExposedThingImpl>())
208+
call.respond(thing, typeInfo<ExposedThing>())
209209
} else {
210210
call.response.status(HttpStatusCode.NotFound)
211211
}
@@ -224,19 +224,14 @@ fun Application.setupRouting(servient: Servient) {
224224
if (property != null) {
225225
if (!property.writeOnly) {
226226
try {
227-
val value = property.read()
227+
228+
thing.
229+
230+
//val value = property.read()
228231
//contentType = getOrDefaultRequestContentType(call.request)
229232
//val content = ContentManager.valueToContent(value, contentType.toString())
230-
when (property) {
231-
is ExposedStringProperty -> call.respond(property.read(), typeInfo<String>())
232-
is ExposedIntProperty -> call.respond(property.read(), typeInfo<Int>())
233-
is ExposedBooleanProperty -> call.respond(property.read(), typeInfo<Boolean>())
234-
is ExposedNumberProperty -> call.respond(property.read(), typeInfo<Double>())
235-
is ExposedObjectProperty -> call.respond(property.read(), typeInfo<Map<*,*>>())
236-
is ExposedNullProperty -> call.respond(property.read(), typeInfo<Any>())
237-
is ExposedArrayProperty -> call.respond(property.read(), typeInfo<List<*>>())
238-
}
239-
}
233+
234+
}
240235
catch (e: ContentCodecException) {
241236
call.response.status(HttpStatusCode.InternalServerError)
242237
} catch (e: ExecutionException) {
@@ -259,15 +254,7 @@ fun Application.setupRouting(servient: Servient) {
259254
//val contentType = getOrDefaultRequestContentType(call.request)
260255
//val content = Content(contentType.toString(), call.receiveChannel().toByteArray())
261256

262-
when (property) {
263-
is ExposedStringProperty -> call.respond(property.write(call.receive<String>()), typeInfo<String>())
264-
is ExposedIntProperty -> call.respond(property.write(call.receive<Int>()), typeInfo<Int>())
265-
is ExposedBooleanProperty -> call.respond(property.write(call.receive<Boolean>()), typeInfo<Boolean>())
266-
is ExposedNumberProperty -> call.respond(property.write(call.receive<Double>()), typeInfo<Double>())
267-
is ExposedObjectProperty -> call.respond(property.write(call.receive<Map<*,*>>()), typeInfo<Map<*,*>>())
268-
is ExposedNullProperty -> call.respond(property.write(call.receive<Any>()), typeInfo<Any>())
269-
is ExposedArrayProperty -> call.respond(property.write(call.receive<List<*>>()), typeInfo<List<*>>())
270-
}
257+
271258
} else {
272259
call.response.status(HttpStatusCode.BadRequest)
273260
}
@@ -287,11 +274,11 @@ fun Application.setupRouting(servient: Servient) {
287274

288275
if(action.input != null){
289276
val input = ContentManager.contentToValue(content, action.input!!)
290-
val newValue = action.invokeAction(input, null)
291-
call.respond(newValue, typeInfo<Any>())
277+
//val newValue = action.invokeAction(input, null)
278+
//call.respond(newValue, typeInfo<Any>())
292279
}else{
293-
val newValue = action.invokeAction(null, null)
294-
call.respond(newValue, typeInfo<Any>())
280+
//val newValue = action.invokeAction(null, null)
281+
//call.respond(newValue, typeInfo<Any>())
295282
}
296283

297284

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package ai.ancf.lmos.wot.binding.http
22
import ai.ancf.lmos.wot.security.SecurityScheme
33
import ai.ancf.lmos.wot.thing.form.Form
4+
import io.ktor.client.*
5+
import io.ktor.client.request.*
6+
import io.ktor.client.statement.*
7+
import io.mockk.coEvery
48
import io.mockk.every
59
import io.mockk.mockk
610
import kotlinx.coroutines.test.runTest
@@ -10,25 +14,30 @@ import kotlin.test.Test
1014

1115
class HttpProtocolClientTest {
1216

13-
private var form: Form? = null
14-
private var statusLine: StatusLine? = null
15-
private var securityScheme: SecurityScheme? = null
17+
private lateinit var form: Form
18+
private lateinit var statusLine: StatusLine
19+
private lateinit var securityScheme: SecurityScheme
20+
private lateinit var httpClient: HttpClient
21+
1622

1723
@BeforeTest
1824
fun setUp() {
1925
form = mockk()
2026
statusLine = mockk()
2127
securityScheme = mockk()
28+
httpClient = mockk()
2229
}
2330

2431
@Test
2532
fun `readResource should create proper request`(): Unit = runTest {
26-
every { form?.href ?: "" } returns "http://localhost/foo"
27-
every { statusLine?.statusCode } returns 200 // HTTP Status OK
33+
every { form.href ?: "" } returns "http://localhost/foo"
34+
every { statusLine.statusCode } returns 200 // HTTP Status OK
2835

29-
val client = HttpProtocolClient()
30-
client.readResource(form!!)
36+
val response : HttpResponse = mockk()
3137

38+
coEvery { httpClient.request("http://localhost/foo", any()) } returns response
3239

40+
val client = HttpProtocolClient(httpClient)
41+
client.readResource(form)
3342
}
3443
}

kotlin-wot-binding-http/src/test/kotlin/http/HttpProtocolServerTest.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ package ai.ancf.lmos.wot.binding.http
22

33
import ai.ancf.lmos.wot.Servient
44
import ai.ancf.lmos.wot.thing.ExposedThingImpl
5+
import ai.ancf.lmos.wot.thing.Thing
56
import ai.ancf.lmos.wot.thing.exposedThing
67
import ai.ancf.lmos.wot.thing.form.Operation
78
import ai.ancf.lmos.wot.thing.form.Operation.READ_PROPERTY
89
import ai.ancf.lmos.wot.thing.form.Operation.WRITE_PROPERTY
9-
import ai.ancf.lmos.wot.thing.schema.PropertyReadHandler
1010
import ai.ancf.lmos.wot.thing.schema.StringSchema
1111
import ai.ancf.lmos.wot.thing.schema.stringSchema
1212
import ai.anfc.lmos.wot.binding.ProtocolServerException
@@ -40,7 +40,6 @@ class HttpProtocolServerTest {
4040
private val exposedThing: ExposedThingImpl = exposedThing(servient) {
4141
intProperty(PROPERTY_NAME) {
4242
observable = true
43-
readHandler = PropertyReadHandler { 2 }
4443
}
4544
action<String, String>(ACTION_NAME)
4645
{
@@ -146,7 +145,7 @@ class HttpProtocolServerTest {
146145

147146
assertEquals(HttpStatusCode.OK, response.status)
148147

149-
val things : List<ExposedThingImpl> = response.body()
148+
val things : List<Thing> = response.body()
150149
assertEquals(1, things.size)
151150

152151

@@ -164,7 +163,7 @@ class HttpProtocolServerTest {
164163
// Perform GET request on "/test"
165164
val response = client.get("/${exposedThing.id}")
166165

167-
val thing : ExposedThingImpl = response.body()
166+
val thing : Thing = response.body()
168167

169168
assertEquals(HttpStatusCode.OK, response.status)
170169
assertEquals(exposedThing.id, thing.id)

kotlin-wot-integration-tests/src/test/kotlin/integration/WoTIntegrationTest.kt

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import ai.ancf.lmos.wot.Servient
44
import ai.ancf.lmos.wot.Wot
55
import ai.ancf.lmos.wot.binding.http.HttpProtocolClientFactory
66
import ai.ancf.lmos.wot.binding.http.HttpProtocolServer
7-
import ai.ancf.lmos.wot.thing.schema.*
7+
import ai.ancf.lmos.wot.thing.schema.DataSchemaValue
8+
import ai.ancf.lmos.wot.thing.schema.stringSchema
9+
import ai.ancf.lmos.wot.thing.schema.toInteractionInputValue
810
import kotlinx.coroutines.test.runTest
911
import kotlin.test.Test
1012
import kotlin.test.assertEquals
@@ -31,8 +33,6 @@ class WoTIntegrationTest() {
3133
stringProperty(PROPERTY_NAME) {
3234
description = "Property description"
3335
minLength = 10
34-
readHandler = PropertyReadHandler { "propertyOutput" }
35-
writeHandler = PropertyWriteHandler { input -> input }
3636
}
3737
action<String, String>(ACTION){
3838
description = "Action description"
@@ -42,13 +42,11 @@ class WoTIntegrationTest() {
4242
output = stringSchema {
4343
description = "Output description"
4444
}
45-
actionHandler = ActionHandler { input, options -> "actionOutput: $input" }
4645
}
4746
}
4847

49-
exposedThing.setPropertyReadHandler(PROPERTY_NAME) { "propertyOutput" }
50-
exposedThing.setPropertyWriteHandler<String>(PROPERTY_NAME) { input -> input }
51-
exposedThing.setActionHandler<String, String>(ACTION) { input, options -> "actionOutput: $input" }
48+
//exposedThing.setPropertyWriteHandler(PROPERTY_NAME) { input -> input }
49+
//exposedThing.setActionHandler(ACTION) { input, options -> "actionOutput: $input" }
5250

5351
servient.start()
5452
servient.addThing(exposedThing)
@@ -66,10 +64,14 @@ class WoTIntegrationTest() {
6664

6765
val readProperty = consumedThing.readProperty(PROPERTY_NAME)
6866

69-
assertEquals("propertyOutput", readProperty.value())
67+
val propertyResponse = readProperty.value() as DataSchemaValue.StringValue
7068

71-
val output = consumedThing.invokeAction(ACTION, "actionInput".toInteractionInputValue())
69+
assertEquals("propertyOutput", propertyResponse.value)
7270

73-
assertEquals("actionOutput + actionInput", output.value())
71+
val output = consumedThing.invokeAction(ACTION, "actionInput".toInteractionInputValue(), null)
72+
73+
val actionResponse = output.value() as DataSchemaValue.StringValue
74+
75+
assertEquals("actionOutput + actionInput", actionResponse.value)
7476
}
7577
}

kotlin-wot/src/main/kotlin/Servient.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import ai.ancf.lmos.wot.thing.Thing
77
import ai.ancf.lmos.wot.thing.filter.DiscoveryMethod.*
88
import ai.ancf.lmos.wot.thing.filter.ThingFilter
99
import ai.ancf.lmos.wot.thing.form.Form
10-
import ai.ancf.lmos.wot.thing.schema.ArraySchema
10+
import ai.ancf.lmos.wot.thing.schema.DataSchemaValue
1111
import ai.ancf.lmos.wot.thing.schema.ExposedThing
1212
import ai.ancf.lmos.wot.thing.schema.ObjectSchema
1313
import ai.anfc.lmos.wot.binding.*
@@ -173,8 +173,9 @@ class Servient(
173173
val form = Form(href = url.toString())
174174
val content = client.readResource(form)
175175
try {
176-
val map = ContentManager.contentToValue(content, ObjectSchema())
177-
return Thing.fromMap(map)
176+
val dataSchemaValue = ContentManager.contentToValue(content, ObjectSchema())
177+
dataSchemaValue as DataSchemaValue.ObjectValue
178+
return Thing.fromMap(dataSchemaValue.value)
178179
} catch (e: ContentCodecException) {
179180
throw ServientException("Error while fetching TD: ${e.message}", e)
180181
}
@@ -192,6 +193,8 @@ class Servient(
192193
return "credentialStore.get(id)"
193194
}
194195

196+
/*
197+
195198
/**
196199
* Calls `url` and expects a Thing Directory there. Returns a list with all found
197200
* [Thing].
@@ -240,6 +243,8 @@ class Servient(
240243
}
241244
}
242245
246+
*/
247+
243248
fun getClientFor(scheme: String): ProtocolClient? {
244249
val factory = clientFactories[scheme]
245250
return if (factory != null) {

0 commit comments

Comments
 (0)