11package ai.ancf.lmos.wot.binding.http
22
33import ai.ancf.lmos.wot.Servient
4- import ai.ancf.lmos.wot.thing.ContentManager
4+ import ai.ancf.lmos.wot.content.Content
5+ import ai.ancf.lmos.wot.content.ContentCodecException
6+ import ai.ancf.lmos.wot.content.ContentManager
57import ai.ancf.lmos.wot.thing.ExposedThing
6- import ai.ancf.lmos.wot.thing.action.ExposedThingAction
78import ai.ancf.lmos.wot.thing.form.Form
89import ai.ancf.lmos.wot.thing.form.Operation
910import ai.ancf.lmos.wot.thing.schema.InteractionAffordance
10- import ai.anfc.lmos.wot.binding.Content
1111import ai.anfc.lmos.wot.binding.ProtocolServer
1212import ai.anfc.lmos.wot.binding.ProtocolServerException
1313import com.fasterxml.jackson.databind.DeserializationFeature
@@ -22,12 +22,13 @@ import io.ktor.server.request.*
2222import io.ktor.server.routing.*
2323import io.ktor.util.reflect.*
2424import org.slf4j.LoggerFactory
25+ import java.util.concurrent.ExecutionException
2526import kotlin.collections.set
2627
2728/* *
2829 * Allows exposing Things via HTTP.
2930 */
30- class HttpProtocolServer (
31+ open class HttpProtocolServer (
3132 private val wait : Boolean = false ,
3233 private val bindHost : String = " 0.0.0.0" ,
3334 private val bindPort : Int = 8080 ,
@@ -36,7 +37,7 @@ class HttpProtocolServer(
3637 val things: MutableMap <String , ExposedThing > = mutableMapOf ()
3738 var started = false
3839 private var server: EmbeddedServer <* , * >? = null
39- private val actualAddresses: List <String > = listOf ()
40+ private var actualAddresses: List <String > = listOf (" http:// $bindHost : $bindPort " )
4041
4142 companion object {
4243 private val log = LoggerFactory .getLogger(HttpProtocolServer ::class .java)
@@ -57,16 +58,16 @@ class HttpProtocolServer(
5758 }
5859
5960 // Expose a thing
60- override suspend fun expose (thing : ExposedThing ) {
61+ override fun expose (thing : ExposedThing ) {
6162 if (! started) throw ProtocolServerException (" Server has not started yet" )
6263
6364 log.info(" Exposing thing '{}'" , thing.id)
6465 things[thing.id] = thing
6566
6667 for (address in actualAddresses) {
67- for (contentType in ContentManager .getOfferedMediaTypes() ) {
68+ for (contentType in ContentManager .offeredMediaTypes ) {
6869 // make reporting of all properties optional?
69- val href = (address + " / " + thing.id).toString() + " /all/properties"
70+ val href = " $address / ${ thing.id} /all/properties"
7071 val form = Form (href = href, contentType = contentType, op = listOf ( Operation .READ_ALL_PROPERTIES ,
7172 Operation .READ_MULTIPLE_PROPERTIES ))
7273
@@ -80,10 +81,8 @@ class HttpProtocolServer(
8081 }
8182 }
8283
83- fun exposeProperties (thing : ExposedThing , address : String , contentType : String ) {
84- val properties = thing.exposedProperties
85-
86- properties.forEach { (name, property) ->
84+ internal fun exposeProperties (thing : ExposedThing , address : String , contentType : String ) {
85+ thing.properties.forEach { (name, property) ->
8786
8887 val href = getHrefWithVariablePattern(address, thing, " properties" , name, property)
8988
@@ -96,7 +95,7 @@ class HttpProtocolServer(
9695
9796 // Create the main form and add it to the property
9897 val form = Form (href = href, contentType = contentType, op = operations)
99- property.forms?.plusAssign( form)
98+ property.forms + = form
10099 log.debug(" Assign '{}' to Property '{}'" , href, name)
101100
102101 // If the property is observable, add an additional form with an observable href
@@ -108,15 +107,14 @@ class HttpProtocolServer(
108107 op = listOf (Operation .OBSERVE_PROPERTY ),
109108 subprotocol = " longpoll"
110109 )
111- property.forms?.plusAssign( observableForm)
110+ property.forms + = observableForm
112111 log.debug(" Assign '{}' to observe Property '{}'" , observableHref, name)
113112 }
114113 }
115114 }
116115
117- fun exposeActions (thing : ExposedThing , address : String , contentType : String ) {
118- val actions: Map <String , ExposedThingAction <* , * >> = thing.exposedActions
119- actions.forEach { (name, action) ->
116+ internal fun exposeActions (thing : ExposedThing , address : String , contentType : String ) {
117+ thing.actions.forEach { (name, action) ->
120118 val href: String = getHrefWithVariablePattern(address, thing, " actions" , name, action)
121119 // Initialize the form using named parameters
122120 val form = Form (
@@ -126,14 +124,13 @@ class HttpProtocolServer(
126124 )
127125
128126 // Add the form to the action
129- action.forms?.plusAssign( form)
127+ action.forms + = form
130128 log.debug(" Assign '{}' to Action '{}'" , href, name)
131129 }
132130 }
133131
134- fun exposeEvents (thing : ExposedThing , address : String , contentType : String ) {
135- val events = thing.exposedEvents
136- events.forEach { (name, event) ->
132+ internal fun exposeEvents (thing : ExposedThing , address : String , contentType : String ) {
133+ thing.events.forEach { (name, event) ->
137134 val href = getHrefWithVariablePattern(address, thing, " events" , name, event)
138135
139136 // Create the form using named parameters directly
@@ -145,7 +142,7 @@ class HttpProtocolServer(
145142 )
146143
147144 // Add the form to the event
148- event.forms?.plusAssign( form)
145+ event.forms + = form
149146 log.debug(" Assign '{}' to Event '{}'" , href, name)
150147 }
151148 }
@@ -159,7 +156,7 @@ class HttpProtocolServer(
159156 ): String {
160157 var variables = " "
161158 val uriVariables = interaction.uriVariables?.keys
162- if (uriVariables != null ) {
159+ if (! uriVariables.isNullOrEmpty() ) {
163160 variables = " {?" + java.lang.String .join(" ," , uriVariables) + " }"
164161 }
165162 return " $address /${thing.id} /$type /$interactionName$variables "
@@ -208,9 +205,23 @@ fun Application.setupRouting(servient: Servient) {
208205 val id = call.parameters[" id" ] ? : return @get call.response.status(HttpStatusCode .BadRequest )
209206 val propertyName = call.parameters[" name" ]
210207 val thing = servient.things[id] ? : return @get call.response.status(HttpStatusCode .NotFound )
211- val property = thing.exposedProperties [propertyName]
208+ val property = thing.properties [propertyName]
212209 if (property != null ) {
213- // call.respond(property.read())
210+ if (! property.writeOnly) {
211+ try {
212+ val value = property.read()
213+ // contentType = getOrDefaultRequestContentType(call.request)
214+ // val content = ContentManager.valueToContent(value, contentType.toString())
215+ call.respond(value, typeInfo<Any >())
216+ }
217+ catch (e: ContentCodecException ) {
218+ call.response.status(HttpStatusCode .InternalServerError )
219+ } catch (e: ExecutionException ) {
220+ call.response.status(HttpStatusCode .InternalServerError )
221+ }
222+ } else {
223+ call.response.status(HttpStatusCode .BadRequest )
224+ }
214225 } else {
215226 call.response.status(HttpStatusCode .NotFound )
216227 }
@@ -219,15 +230,13 @@ fun Application.setupRouting(servient: Servient) {
219230 val id = call.parameters[" id" ] ? : return @put call.response.status(HttpStatusCode .BadRequest )
220231 val propertyName = call.parameters[" name" ]
221232 val thing = servient.things[id] ? : return @put call.response.status(HttpStatusCode .NotFound )
222- val property = thing.exposedProperties [propertyName]
233+ val property = thing.properties [propertyName]
223234 if (property != null ) {
224235 if (! property.readOnly) {
225- val contentType = getOrDefaultRequestContentType(call.request)
226- val content = Content (contentType.toString(), call.receiveText())
227- val input = ContentManager .contentToValue(content, property)
228-
229- // val newValue = property.write(input)
230- // call.respond(newValue)
236+ // val contentType = getOrDefaultRequestContentType(call.request)
237+ // val content = Content(contentType.toString(), call.receiveChannel().toByteArray())
238+ // val newValue = property.write(call.receive())
239+ // call.respond(newValue, typeInfo<Any>())
231240 } else {
232241 call.response.status(HttpStatusCode .BadRequest )
233242 }
@@ -240,10 +249,26 @@ fun Application.setupRouting(servient: Servient) {
240249 val id = call.parameters[" id" ] ? : return @post call.response.status(HttpStatusCode .BadRequest )
241250 val actionName = call.parameters[" name" ]
242251 val thing = servient.things[id] ? : return @post call.response.status(HttpStatusCode .NotFound )
243- val action = thing.exposedActions [actionName]
252+ val action = thing.actions [actionName]
244253 if (action != null ) {
245- val input = call.receive<Any >()
246- call.respond(" TODO Return action response" , typeInfo<String >())
254+ val requestContentType = getOrDefaultRequestContentType(call.request).toString()
255+ val content = Content (requestContentType, call.receive())
256+
257+ if (action.input != null ){
258+ val input = ContentManager .contentToValue(content, action.input!! )
259+ val newValue = action.invokeAction(null , null )
260+ call.respond(newValue, typeInfo<Any >())
261+ }else {
262+ val newValue = action.invokeAction(null , null )
263+ call.respond(newValue, typeInfo<Any >())
264+ }
265+
266+
267+ /*
268+ val options = mapOf<String, Map<String, Any>>(
269+ "uriVariables" to parseUrlParameters(request.queryMap().toMap(), action.uriVariables)
270+ )
271+ */
247272 } else {
248273 call.response.status(HttpStatusCode .NotFound )
249274 }
@@ -272,4 +297,25 @@ private fun getOrDefaultRequestContentType(request: RoutingRequest): ContentType
272297 } else {
273298 contentType
274299 }
275- }
300+ }
301+
302+ /*
303+ private fun parseUrlParameters(
304+ urlParams: Map<String, Array<String>>,
305+ uriVariables: MutableMap<String, DataSchema<Any>>?
306+ ): Map<String, Any> {
307+ return urlParams.mapNotNull { (name, urlValue) ->
308+ val uriVariable = uriVariables?.get(name)
309+ val type = uriVariable["type"]
310+
311+ val value: Any = when (type) {
312+ "integer", "number" -> urlValue.firstOrNull()?.toIntOrNull()
313+ "string" -> urlValue.firstOrNull()
314+ else -> {
315+ null
316+ }
317+ }
318+ name to value
319+ }.toMap()
320+ }
321+ */
0 commit comments