diff --git a/kotlin-wot-integration-tests/src/main/kotlin/integration/ChatAgent.kt b/kotlin-wot-integration-tests/src/main/kotlin/integration/ChatAgent.kt
index 4cb09b1..e33e541 100644
--- a/kotlin-wot-integration-tests/src/main/kotlin/integration/ChatAgent.kt
+++ b/kotlin-wot-integration-tests/src/main/kotlin/integration/ChatAgent.kt
@@ -25,6 +25,7 @@ import org.springframework.stereotype.Component
@Thing(id="chatagent", title="Chat Agent",
description="A chat agent.", type= LMOSThingType.AGENT)
@Context(prefix = LMOSContext.prefix, url = LMOSContext.url)
+@Link(href = "lmos/capabilities", rel = "service-meta", type = "application/json")
@VersionInfo(instance = "1.0.0")
@Component
class ChatAgent(agentProvider: AgentProvider, @Property(readOnly = true)
diff --git a/kotlin-wot-reflection/src/main/kotlin/reflection/ExposedThingBuilder.kt b/kotlin-wot-reflection/src/main/kotlin/reflection/ExposedThingBuilder.kt
index 9708e1f..e786cc6 100644
--- a/kotlin-wot-reflection/src/main/kotlin/reflection/ExposedThingBuilder.kt
+++ b/kotlin-wot-reflection/src/main/kotlin/reflection/ExposedThingBuilder.kt
@@ -4,6 +4,7 @@ import ai.ancf.lmos.wot.JsonMapper
import ai.ancf.lmos.wot.Wot
import ai.ancf.lmos.wot.reflection.annotations.*
import ai.ancf.lmos.wot.reflection.annotations.Context
+import ai.ancf.lmos.wot.reflection.annotations.Link
import ai.ancf.lmos.wot.reflection.annotations.VersionInfo
import ai.ancf.lmos.wot.thing.DEFAULT_CONTEXT
import ai.ancf.lmos.wot.thing.ExposedThing
@@ -77,6 +78,26 @@ object ExposedThingBuilder {
if(versionInfoAnnotation != null){
version = ai.ancf.lmos.wot.thing.schema.VersionInfo(versionInfoAnnotation.instance, versionInfoAnnotation.model)
}
+ clazz.findAnnotation()?.let { linkAnnotation ->
+ links.add(ai.ancf.lmos.wot.thing.schema.Link(
+ href = linkAnnotation.href,
+ type = linkAnnotation.type,
+ rel = linkAnnotation.rel,
+ anchor = linkAnnotation.anchor,
+ sizes = linkAnnotation.sizes,
+ hreflang = linkAnnotation.hreflang.toList()
+ ))
+ }
+ clazz.findAnnotation()?.values?.forEach { linkAnnotation ->
+ links.add(ai.ancf.lmos.wot.thing.schema.Link(
+ href = linkAnnotation.href,
+ type = linkAnnotation.type,
+ rel = linkAnnotation.rel,
+ anchor = linkAnnotation.anchor,
+ sizes = linkAnnotation.sizes,
+ hreflang = linkAnnotation.hreflang.toList()
+ ))
+ }
// 3. Inspect the properties of the class and find @Property annotations
clazz.memberProperties.forEach { property ->
val propertyAnnotation = property.findAnnotation()
diff --git a/kotlin-wot-reflection/src/main/kotlin/reflection/annotations/Annotations.kt b/kotlin-wot-reflection/src/main/kotlin/reflection/annotations/Annotations.kt
index 57f0838..7c96410 100644
--- a/kotlin-wot-reflection/src/main/kotlin/reflection/annotations/Annotations.kt
+++ b/kotlin-wot-reflection/src/main/kotlin/reflection/annotations/Annotations.kt
@@ -15,6 +15,21 @@ annotation class Context(val prefix: String, val url : String)
@Retention(AnnotationRetention.RUNTIME)
annotation class VersionInfo(val instance : String, val model : String = "")
+@Target(AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.RUNTIME)
+annotation class Link(
+ val href: String,
+ val type: String = "",
+ val rel: String = "",
+ val anchor: String = "",
+ val sizes: String = "",
+ val hreflang: Array = []
+)
+
+@Target(AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.RUNTIME)
+annotation class Links(val values: Array)
+
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class Property(val title: String = "", val description: String = "", val readOnly: Boolean = false, val writeOnly: Boolean = false)
diff --git a/kotlin-wot-reflection/src/test/kotlin/reflection/ComplexThingTest.kt b/kotlin-wot-reflection/src/test/kotlin/reflection/ComplexThingTest.kt
index 3b3ce61..99a585a 100644
--- a/kotlin-wot-reflection/src/test/kotlin/reflection/ComplexThingTest.kt
+++ b/kotlin-wot-reflection/src/test/kotlin/reflection/ComplexThingTest.kt
@@ -35,6 +35,7 @@ class ComplexThingTest {
assertEquals("Complex Thing", thingDescription.title, "ThingDescription title should match")
assertEquals("A thing with complex properties, actions, and events.", thingDescription.description, "ThingDescription description should match")
assertEquals("1.0.0", thingDescription.version?.instance)
+ assertEquals(listOf(Link("my/link", "my/type", "my-rel", "my-anchor", "my-sizes", listOf("my-lang-1", "my-lang-2"))), thingDescription.links)
}
@Test
diff --git a/kotlin-wot-reflection/src/test/kotlin/reflection/SimpleThingTest.kt b/kotlin-wot-reflection/src/test/kotlin/reflection/SimpleThingTest.kt
index cd63503..e330b10 100644
--- a/kotlin-wot-reflection/src/test/kotlin/reflection/SimpleThingTest.kt
+++ b/kotlin-wot-reflection/src/test/kotlin/reflection/SimpleThingTest.kt
@@ -8,7 +8,9 @@ import ai.ancf.lmos.wot.content.toJsonContent
import ai.ancf.lmos.wot.reflection.things.SimpleThing
import ai.ancf.lmos.wot.thing.ExposedThing
import ai.ancf.lmos.wot.thing.schema.ContentListener
+import ai.ancf.lmos.wot.thing.schema.Link
import ai.ancf.lmos.wot.thing.schema.StringSchema
+import ai.ancf.lmos.wot.thing.schema.WoTThingDescription
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import java.util.concurrent.CountDownLatch
@@ -21,6 +23,7 @@ class SimpleThingTest {
lateinit var wot: Wot
lateinit var simpleThing: SimpleThing
lateinit var exposedThing: ExposedThing
+ lateinit var thingDescription: WoTThingDescription
@BeforeTest
fun setUp() = runTest {
@@ -35,11 +38,22 @@ class SimpleThingTest {
// Generate ThingDescription from the class
exposedThing = ExposedThingBuilder.createExposedThing(wot, simpleThing, SimpleThing::class) as ExposedThing
+ thingDescription = exposedThing.getThingDescription()
servient.addThing(exposedThing)
servient.expose("simpleThing")
}
+ @Test
+ fun `test ThingDescription creation for SimpleThing`() {
+ // Validate Thing metadata
+ assertEquals("simpleThing", thingDescription.id, "ThingDescription ID should match the class ID")
+ assertEquals("Simple Thing", thingDescription.title, "ThingDescription title should match")
+ assertEquals("A thing with complex properties, actions, and events.", thingDescription.description, "ThingDescription description should match")
+ assertEquals("1.0.0", thingDescription.version?.instance)
+ assertEquals(listOf(Link("my/link", "my/type", "my-rel", "my-anchor", "my-sizes", listOf("my-lang-1", "my-lang-2"))), thingDescription.links)
+ }
+
@Test
fun `Read mutable property`() = runTest {
val content = exposedThing.handleReadProperty("mutableProperty")
diff --git a/kotlin-wot-reflection/src/test/kotlin/reflection/things/ComplexThing.kt b/kotlin-wot-reflection/src/test/kotlin/reflection/things/ComplexThing.kt
index 39cbbb5..2116772 100644
--- a/kotlin-wot-reflection/src/test/kotlin/reflection/things/ComplexThing.kt
+++ b/kotlin-wot-reflection/src/test/kotlin/reflection/things/ComplexThing.kt
@@ -13,6 +13,16 @@ import kotlin.random.Random
title = "Complex Thing",
description = "A thing with complex properties, actions, and events."
)
+@Links(
+ values = [Link(
+ href = "my/link",
+ rel = "my-rel",
+ type = "my/type",
+ anchor = "my-anchor",
+ sizes = "my-sizes",
+ hreflang = ["my-lang-1", "my-lang-2"]
+ )]
+)
@VersionInfo(instance = "1.0.0")
class ComplexThing(@Property(readOnly = true) val constructorProperty: String = "Hello World") {
diff --git a/kotlin-wot-reflection/src/test/kotlin/reflection/things/SimpleThing.kt b/kotlin-wot-reflection/src/test/kotlin/reflection/things/SimpleThing.kt
index 6c8812e..0ea02b3 100644
--- a/kotlin-wot-reflection/src/test/kotlin/reflection/things/SimpleThing.kt
+++ b/kotlin-wot-reflection/src/test/kotlin/reflection/things/SimpleThing.kt
@@ -10,6 +10,14 @@ import kotlinx.coroutines.flow.flow
title = "Simple Thing",
description = "A thing with complex properties, actions, and events."
)
+@Link(
+ href = "my/link",
+ rel = "my-rel",
+ type = "my/type",
+ anchor = "my-anchor",
+ sizes = "my-sizes",
+ hreflang = ["my-lang-1", "my-lang-2"]
+)
@VersionInfo(instance = "1.0.0")
class SimpleThing {
diff --git a/kotlin-wot/src/main/kotlin/thing/ThingDescription.kt b/kotlin-wot/src/main/kotlin/thing/ThingDescription.kt
index cf678ee..638d4b8 100644
--- a/kotlin-wot/src/main/kotlin/thing/ThingDescription.kt
+++ b/kotlin-wot/src/main/kotlin/thing/ThingDescription.kt
@@ -74,7 +74,7 @@ data class ThingDescription @JsonCreator constructor(
@JsonInclude(NON_EMPTY) override var created: String? = null,
@JsonInclude(NON_EMPTY) override var modified: String? = null,
@JsonInclude(NON_EMPTY) override var support: String? = null,
- @JsonInclude(NON_EMPTY) override var links: List? = null,
+ @JsonInclude(NON_EMPTY) override var links: MutableList = mutableListOf(),
@JsonFormat(with = [JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY]) @JsonInclude(NON_EMPTY) override var profile: List? = null,
@JsonInclude(NON_EMPTY) override var schemaDefinitions: MutableMap>? = null,
@JsonInclude(NON_EMPTY) override var uriVariables: MutableMap>? = null
diff --git a/kotlin-wot/src/main/kotlin/thing/schema/WoTThingDescription.kt b/kotlin-wot/src/main/kotlin/thing/schema/WoTThingDescription.kt
index 6b01146..5a0983b 100644
--- a/kotlin-wot/src/main/kotlin/thing/schema/WoTThingDescription.kt
+++ b/kotlin-wot/src/main/kotlin/thing/schema/WoTThingDescription.kt
@@ -103,7 +103,7 @@ interface WoTThingDescription : BaseSchema {
* @return an array of links.
*/
@get:JsonInclude(JsonInclude.Include.NON_EMPTY)
- var links: List? // Optional: Array of Link
+ var links: MutableList // Optional: Array of Link
/**
* Set of form hypermedia controls that describe how an operation can be performed.