Change query param to query path
Change-Id: Ib01eb0b271924c11d3755029644ade293d520c47
diff --git a/README.md b/README.md
index a81510d..a1d476a 100644
--- a/README.md
+++ b/README.md
@@ -124,14 +124,65 @@
## API Endpoints
+### POST /query/:cfg
+
+Apply a cascade of query mappings to a JSON object. The `:cfg` path parameter specifies which mapping lists to apply and in what order, using a compact serialization format.
+
+**cfg format:** `id:dir[:foundryA:layerA:foundryB:layerB]` entries separated by `;`
+
+- `id`: ID of the mapping list
+- `dir`: Direction (`atob` or `btoa`)
+- Optional foundry/layer overrides (annotation mappings use 6 fields, corpus mappings use 4 fields with `fieldA:fieldB`)
+
+When override fields are omitted, defaults from the YAML mapping list are used.
+
+Request body: JSON object to transform
+
+Example request:
+
+```http
+POST /query/stts-upos:atob;other-mapper:btoa HTTP/1.1
+Content-Type: application/json
+
+{
+ "@type": "koral:token",
+ "wrap": {
+ "@type": "koral:term",
+ "foundry": "opennlp",
+ "key": "PIDAT",
+ "layer": "p",
+ "match": "match:eq"
+ }
+}
+```
+
+### POST /response/:cfg
+
+Apply a cascade of response mappings to a JSON object. The `:cfg` path parameter uses the same format as `/query/:cfg`.
+
+This endpoint processes response snippets by applying term mappings to annotations within HTML snippet markup, and enriches corpus fields for corpus mappings.
+
+Request body: JSON object (with `snippet` field for annotation mappings, or `fields` for corpus mappings)
+
+Example request:
+
+```http
+POST /response/stts-upos:btoa HTTP/1.1
+Content-Type: application/json
+
+{
+ "snippet": "<span title=\"marmot/m:gender:masc\">Der</span>"
+}
+```
+
### POST /:map/query
-Transform a JSON object using the specified mapping list.
+Transform a JSON object using a single mapping list.
Parameters:
- `:map`: ID of the mapping list to use
-- `dir` (query): Direction of transformation (atob or `btoa`, default: `atob`)
+- `dir` (query): Direction of transformation (`atob` or `btoa`, default: `atob`)
- `foundryA` (query): Override default foundryA from mapping list
- `foundryB` (query): Override default foundryB from mapping list
- `layerA` (query): Override default layerA from mapping list
@@ -188,12 +239,12 @@
### POST /:map/response
-Transform JSON response objects using the specified mapping list. This endpoint processes response snippets by applying term mappings to annotations within HTML snippet markup.
+Transform JSON response objects using a single mapping list. This endpoint processes response snippets by applying term mappings to annotations within HTML snippet markup.
Parameters:
- `:map`: ID of the mapping list to use
-- `dir` (query): Direction of transformation (atob or `btoa`, default: `atob`)
+- `dir` (query): Direction of transformation (`atob` or `btoa`, default: `atob`)
- `foundryA` (query): Override default foundryA from mapping list
- `foundryB` (query): Override default foundryB from mapping list
- `layerA` (query): Override default layerA from mapping list
@@ -220,9 +271,13 @@
}
```
+### GET /
+
+Serves the configuration page for the Kalamar plugin integration. This HTML page allows selecting mapping lists and configuring their parameters. The JavaScript registers KorAP pipes using the path-based `/query/:cfg` and `/response/:cfg` endpoints.
+
### GET /:map
-Serves the Kalamar plugin integration page. This HTML page includes:
+Serves the Kalamar plugin integration page for a single mapping list. This HTML page includes:
- Plugin information and available mapping lists
- JavaScript integration code for Kalamar
@@ -230,6 +285,10 @@
The SDK script and server data-attribute in the HTML are determined by the configuration file's `sdk` and `server` values, with fallback to default endpoints if not specified.
+### GET /health
+
+Health check endpoint. Returns `OK` with HTTP 200.
+
## Supported Mappings
### `mappings/stts-upos.yaml`
diff --git a/cmd/koralmapper/main.go b/cmd/koralmapper/main.go
index 732cc2b..d8e51a6 100644
--- a/cmd/koralmapper/main.go
+++ b/cmd/koralmapper/main.go
@@ -312,9 +312,9 @@
// Static file serving from embedded FS
app.Get("/static/*", handleStaticFile())
- // Composite cascade transformation endpoints
- app.Post("/query", handleCompositeQueryTransform(m, yamlConfig.Lists))
- app.Post("/response", handleCompositeResponseTransform(m, yamlConfig.Lists))
+ // Composite cascade transformation endpoints (cfg in path)
+ app.Post("/query/:cfg", handleCompositeQueryTransform(m, yamlConfig.Lists))
+ app.Post("/response/:cfg", handleCompositeResponseTransform(m, yamlConfig.Lists))
// Transformation endpoint
app.Post("/:map/query", handleTransform(m))
@@ -406,7 +406,7 @@
func handleCompositeQueryTransform(m *mapper.Mapper, lists []config.MappingList) fiber.Handler {
return func(c *fiber.Ctx) error {
- cfgRaw := c.Query("cfg", "")
+ cfgRaw := c.Params("cfg")
if len(cfgRaw) > maxParamLength {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": fmt.Sprintf("cfg too long (max %d bytes)", maxParamLength),
@@ -465,7 +465,7 @@
func handleCompositeResponseTransform(m *mapper.Mapper, lists []config.MappingList) fiber.Handler {
return func(c *fiber.Ctx) error {
- cfgRaw := c.Query("cfg", "")
+ cfgRaw := c.Params("cfg")
if len(cfgRaw) > maxParamLength {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": fmt.Sprintf("cfg too long (max %d bytes)", maxParamLength),
diff --git a/cmd/koralmapper/main_test.go b/cmd/koralmapper/main_test.go
index 6121fe3..ecbb5e8 100644
--- a/cmd/koralmapper/main_test.go
+++ b/cmd/koralmapper/main_test.go
@@ -1595,7 +1595,7 @@
}{
{
name: "cascades two query mappings",
- url: "/query?cfg=step1:atob;step2:atob",
+ url: "/query/step1:atob;step2:atob",
expectedCode: http.StatusOK,
input: `{
"@type": "koral:token",
@@ -1619,33 +1619,8 @@
},
},
{
- name: "empty cfg returns input unchanged",
- url: "/query?cfg=",
- expectedCode: http.StatusOK,
- input: `{
- "@type": "koral:token",
- "wrap": {
- "@type": "koral:term",
- "foundry": "opennlp",
- "key": "PIDAT",
- "layer": "p",
- "match": "match:eq"
- }
- }`,
- expected: map[string]any{
- "@type": "koral:token",
- "wrap": map[string]any{
- "@type": "koral:term",
- "foundry": "opennlp",
- "key": "PIDAT",
- "layer": "p",
- "match": "match:eq",
- },
- },
- },
- {
name: "invalid cfg returns bad request",
- url: "/query?cfg=missing:atob",
+ url: "/query/missing:atob",
expectedCode: http.StatusBadRequest,
input: `{"@type": "koral:token"}`,
expected: map[string]any{
@@ -1703,7 +1678,7 @@
}{
{
name: "cascades two response mappings",
- url: "/response?cfg=resp-step1:atob;resp-step2:atob",
+ url: "/response/resp-step1:atob;resp-step2:atob",
expectedCode: http.StatusOK,
input: `{
"fields": [{
@@ -1724,27 +1699,8 @@
},
},
{
- name: "empty cfg returns input unchanged",
- url: "/response?cfg=",
- expectedCode: http.StatusOK,
- input: `{
- "fields": [{
- "@type": "koral:field",
- "key": "textClass",
- "value": "novel",
- "type": "type:string"
- }]
- }`,
- assertBody: func(t *testing.T, actual map[string]any) {
- fields := actual["fields"].([]any)
- require.Len(t, fields, 1)
- assert.Equal(t, "textClass", fields[0].(map[string]any)["key"])
- assert.Equal(t, "novel", fields[0].(map[string]any)["value"])
- },
- },
- {
name: "invalid cfg returns bad request",
- url: "/response?cfg=resp-step1",
+ url: "/response/resp-step1",
expectedCode: http.StatusBadRequest,
input: `{"fields": []}`,
assertBody: func(t *testing.T, actual map[string]any) {