Skip to content

Commit 110cd12

Browse files
committed
URI scheme support in CatalogConfig
Implement equivalent transparent file:// URI parsing support in CatalogConfig.fromPath and BasicCatalog.getConfig. Unsupported schemes are correctly rejected, and robust unit tests are included. Port of Python SDK commit 90a0a19
1 parent 23399c5 commit 110cd12

4 files changed

Lines changed: 128 additions & 4 deletions

File tree

agent_sdks/kotlin/src/main/kotlin/com/google/a2ui/basic_catalog/BasicCatalogProvider.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import com.google.a2ui.core.schema.A2uiConstants
2323
import com.google.a2ui.core.schema.A2uiVersion
2424
import com.google.a2ui.core.schema.CatalogConfig
2525
import com.google.a2ui.core.schema.SchemaResourceLoader
26+
import com.google.a2ui.core.schema.resolveExamplesPath
2627
import kotlinx.serialization.json.JsonObject
2728
import kotlinx.serialization.json.JsonPrimitive
2829

@@ -91,6 +92,6 @@ object BasicCatalog {
9192
CatalogConfig(
9293
name = BASIC_CATALOG_NAME,
9394
provider = BundledCatalogProvider(version),
94-
examplesPath = examplesPath,
95+
examplesPath = resolveExamplesPath(examplesPath),
9596
)
9697
}

agent_sdks/kotlin/src/main/kotlin/com/google/a2ui/core/schema/Catalog.kt

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,47 @@ data class CatalogConfig(
4242
/** Create a [CatalogConfig] using a [FileSystemCatalogProvider]. */
4343
@JvmStatic
4444
@JvmOverloads
45-
fun fromPath(name: String, catalogPath: String, examplesPath: String? = null): CatalogConfig =
46-
CatalogConfig(name, FileSystemCatalogProvider(catalogPath), examplesPath)
45+
fun fromPath(name: String, catalogPath: String, examplesPath: String? = null): CatalogConfig {
46+
val uri =
47+
try {
48+
java.net.URI(catalogPath)
49+
} catch (e: Exception) {
50+
null
51+
}
52+
val scheme = uri?.scheme?.lowercase()
53+
54+
val provider =
55+
when {
56+
scheme == null || scheme == "file" -> {
57+
val path =
58+
if (scheme == "file") java.nio.file.Paths.get(uri).toString() else catalogPath
59+
FileSystemCatalogProvider(path)
60+
}
61+
scheme == "http" || scheme == "https" ->
62+
throw NotImplementedError("HTTP support is coming soon.")
63+
else -> throw IllegalArgumentException("Unsupported catalog URL scheme: $catalogPath")
64+
}
65+
66+
return CatalogConfig(name, provider, resolveExamplesPath(examplesPath))
67+
}
68+
}
69+
}
70+
71+
internal fun resolveExamplesPath(path: String?): String? {
72+
if (path != null) {
73+
val uri =
74+
try {
75+
java.net.URI(path)
76+
} catch (e: Exception) {
77+
null
78+
}
79+
val scheme = uri?.scheme?.lowercase()
80+
if (scheme == null || scheme == "file") {
81+
return if (scheme == "file") java.nio.file.Paths.get(uri).toString() else path
82+
}
83+
throw IllegalArgumentException("Unsupported examples URL scheme: $path")
4784
}
85+
return null
4886
}
4987

5088
/** Represents a processed component catalog with its schema. */

agent_sdks/kotlin/src/main/kotlin/com/google/a2ui/core/schema/CatalogProvider.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ interface A2uiCatalogProvider {
3232
}
3333

3434
/** Loads catalog definition from the local filesystem. */
35-
class FileSystemCatalogProvider(private val path: String) : A2uiCatalogProvider {
35+
class FileSystemCatalogProvider(val path: String) : A2uiCatalogProvider {
3636
override fun load(): JsonObject {
3737
try {
3838
val file = File(path)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.a2ui.core.schema
18+
19+
import kotlin.test.Test
20+
import kotlin.test.assertEquals
21+
import kotlin.test.assertFailsWith
22+
import kotlin.test.assertNull
23+
import kotlin.test.assertTrue
24+
25+
class CatalogTest {
26+
27+
@Test
28+
fun resolvesExamplesPathHandling() {
29+
assertNull(resolveExamplesPath(null))
30+
assertEquals("/absolute/examples", resolveExamplesPath("/absolute/examples"))
31+
assertEquals("/absolute/examples", resolveExamplesPath("file:///absolute/examples"))
32+
33+
val e =
34+
assertFailsWith<IllegalArgumentException> {
35+
resolveExamplesPath("https://a2ui.org/examples")
36+
}
37+
assertTrue(e.message?.contains("Unsupported examples URL scheme") == true)
38+
}
39+
40+
@Test
41+
fun stripsPrefixFromCatalogConfigFromPathSchemes() {
42+
// Test local path
43+
var config =
44+
CatalogConfig.fromPath(name = "test_file", catalogPath = "relative_path/to/catalog.json")
45+
assertEquals(
46+
"relative_path/to/catalog.json",
47+
(config.provider as FileSystemCatalogProvider).path,
48+
)
49+
50+
// Test file:// scheme
51+
config =
52+
CatalogConfig.fromPath(
53+
name = "test_file",
54+
catalogPath = "file:///absolute_path/to/catalog.json",
55+
)
56+
assertEquals(
57+
"/absolute_path/to/catalog.json",
58+
(config.provider as FileSystemCatalogProvider).path,
59+
)
60+
61+
// Test HTTP raises NotImplementedError
62+
val eHttp =
63+
assertFailsWith<NotImplementedError> {
64+
CatalogConfig.fromPath(name = "test_http", catalogPath = "http://a2ui.org/catalog.json")
65+
}
66+
assertTrue(eHttp.message?.contains("HTTP support is coming soon.") == true)
67+
68+
// Test unsupported scheme raises IllegalArgumentException
69+
val eFtp =
70+
assertFailsWith<IllegalArgumentException> {
71+
CatalogConfig.fromPath(name = "test_ftp", catalogPath = "ftp://a2ui.org/catalog.json")
72+
}
73+
assertTrue(eFtp.message?.contains("Unsupported catalog URL scheme") == true)
74+
}
75+
76+
@Test
77+
fun resolvesExamplesPathInBasicCatalogGetConfig() {
78+
val config =
79+
com.google.a2ui.basic_catalog.BasicCatalog.getConfig(
80+
version = A2uiVersion.VERSION_0_9,
81+
examplesPath = "file:///absolute/examples",
82+
)
83+
assertEquals("/absolute/examples", config.examplesPath)
84+
}
85+
}

0 commit comments

Comments
 (0)