Change query param to query path

Change-Id: Ib01eb0b271924c11d3755029644ade293d520c47
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) {