Repository: avenga/couper
Pull request #471 (2022-04-05 11:14:44)

Api basepath share
Allow api blocks sharing the same base_path

---
<details>
    <summary>Reviewer checklist</summary>
    <ul>
        <li>Read PR description: a summary about the changes is required</li>
        <li>Changelog updated</li>
        <li>Documentation: docs/{Reference, Cli, ...}, Docker and cli help/usage</li>
        <li>Pulled branch, manually tested</li>
        <li>Verified requirements are met</li>
        <li>Reviewed the code</li>
        <li>Reviewed the related tests</li>
    </ul>
</details>



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b3f25f6c..2fea2185 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,8 @@ Unreleased changes are available as `avenga/couper:edge` container.
 
 * **Fixed**
   * missing error handling while loading a given `ca_file` ([#460](https://github.com/avenga/couper/pull/460))
+  * allow [`api` blocks](./docs/REFERENCE.md#api-block) sharing the same `base_path` ([#471](https://github.com/avenga/couper/pull/471))
+
 
 ---
 
diff --git a/config/runtime/endpoint.go b/config/runtime/endpoint.go
index 83f2692b..66059714 100644
--- a/config/runtime/endpoint.go
+++ b/config/runtime/endpoint.go
@@ -35,10 +35,6 @@ func newEndpointMap(srvConf *config.Server, serverOptions *server.Options) (endp
 
 		isAPIBasePathUniqueToFilesAndSPA := basePath != filesBasePath && basePath != spaBasePath
 
-		if _, ok := apiBasePaths[basePath]; ok {
-			return nil, fmt.Errorf("API paths must be unique")
-		}
-
 		apiBasePaths[basePath] = struct{}{}
 
 		for _, epConf := range apiConf.Endpoints {
diff --git a/docs/REFERENCE.md b/docs/REFERENCE.md
index 1433c876..7d59db76 100644
--- a/docs/REFERENCE.md
+++ b/docs/REFERENCE.md
@@ -110,7 +110,7 @@ as json error with an error body payload. This can be customized via `error_file
 
 | Attribute(s) | Type |Default|Description|Characteristic(s)| Example|
 | :------------------------------  | :--------------- | :--------------- | :--------------- | :--------------- | :--------------- |
-|`base_path`|string|-|Configures the path prefix for all requests.|&#9888; Must be unique if multiple `api` blocks are defined.| `base_path = "/v1"`|
+|`base_path`|string|-|Configures the path prefix for all requests.|| `base_path = "/v1"`|
 | `error_file` |string|-|Location of the error file template.|-|`error_file = "./my_error_body.json"`|
 | `access_control` |list|-|Sets predefined [Access Control](#access-control) for `api` block context.|&#9888; Inherited by nested blocks.| `access_control = ["foo"]`|
 | `allowed_methods` | tuple of string | `["*"]` == `["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]` | Sets allowed methods as _default_ for all contained endpoints. Requests with a method that is not allowed result in an error response with a `405 Method Not Allowed` status. | The default value `*` can be combined with additional methods. Methods are matched case-insensitively. `Access-Control-Allow-Methods` is only sent in response to a [CORS](#cors-block) preflight request, if the method requested by `Access-Control-Request-Method` is an allowed method. | `allowed_methods = ["GET", "POST"]` or `allowed_methods = ["*", "BREW"]` |


Test patch:
diff --git a/config/configload/validate_test.go b/config/configload/validate_test.go
index b4a1d95d..4a69d3a0 100644
--- a/config/configload/validate_test.go
+++ b/config/configload/validate_test.go
@@ -114,7 +114,7 @@ func TestLabels(t *testing.T) {
 			"",
 		},
 		{
-			"multiple anonymous api blocks",
+			"multiple anonymous api blocks, different base_path",
 			`server "test" {
 			   api {
 			     base_path = "/foo"
@@ -125,6 +125,27 @@ func TestLabels(t *testing.T) {
 			 }`,
 			"",
 		},
+		{
+			"multiple anonymous api blocks (sharing base_path)",
+			`server "test" {
+			   api {}
+			   api {}
+			 }`,
+			"",
+		},
+		{
+			"api blocks sharing base_path",
+			`server "test" {
+			   api {
+			     base_path = "/foo"
+			   }
+			   api {
+			     base_path = "/foo"
+			   }
+			 }`,
+			"",
+		},
+
 		{
 			"mixed labelled api blocks",
 			`server "test" {