From 7d856e0546e111d7d6f2b1ff7a6ea4eda35199a4 Mon Sep 17 00:00:00 2001 From: shbernal Date: Sun, 7 Jun 2026 00:35:04 +0200 Subject: [PATCH] Support MantisBT REST API root URLs --- README.md | 8 ++++++++ internal/mantis/client.go | 12 ++++++++++++ internal/mantis/client_test.go | 33 +++++++++++++++++++++++++++++++++ skills/mantisbt/SKILL.md | 4 ++++ 4 files changed, 57 insertions(+) diff --git a/README.md b/README.md index 5a02681..878117d 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,14 @@ export MANTISBT_URL="https://mantis.example.com" export MANTISBT_TOKEN="your-api-token" ``` +`MANTISBT_URL` can point at the MantisBT web root, or directly at a REST API +root when the server exposes one: + +```bash +export MANTISBT_URL="https://mantis.example.com/api/rest" +export MANTISBT_URL="https://mantis.example.com/api/rest/index.php" +``` + Every command also accepts explicit connection flags, which take precedence over the environment: diff --git a/internal/mantis/client.go b/internal/mantis/client.go index 94a8cb7..8e795e3 100644 --- a/internal/mantis/client.go +++ b/internal/mantis/client.go @@ -112,6 +112,9 @@ func (c *Client) buildURL(path string, params map[string]string) string { if !strings.HasPrefix(route, "/") { route = "/" + route } + if isRESTBaseURL(base) && strings.HasPrefix(route, "/api/rest/") { + route = strings.TrimPrefix(route, "/api/rest") + } full := base + route if len(params) > 0 { q := url.Values{} @@ -122,3 +125,12 @@ func (c *Client) buildURL(path string, params map[string]string) string { } return full } + +func isRESTBaseURL(base string) bool { + u, err := url.Parse(base) + if err != nil { + return false + } + path := strings.TrimRight(u.Path, "/") + return strings.HasSuffix(path, "/api/rest") || strings.HasSuffix(path, "/api/rest/index.php") +} diff --git a/internal/mantis/client_test.go b/internal/mantis/client_test.go index 89a4b81..6b6485b 100644 --- a/internal/mantis/client_test.go +++ b/internal/mantis/client_test.go @@ -13,3 +13,36 @@ func TestBuildURL(t *testing.T) { t.Fatalf("unexpected url with leading-slash normalisation and query: %s", got) } } + +func TestBuildURLWithRESTBase(t *testing.T) { + tests := []struct { + name string + base string + want string + }{ + { + name: "rest root", + base: "https://mantis.example.com/api/rest", + want: "https://mantis.example.com/api/rest/users/me", + }, + { + name: "rest index", + base: "https://mantis.example.com/api/rest/index.php", + want: "https://mantis.example.com/api/rest/index.php/users/me", + }, + { + name: "rest index under subdirectory", + base: "https://mantis.example.com/mantisbt/api/rest/index.php/", + want: "https://mantis.example.com/mantisbt/api/rest/index.php/users/me", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + c := NewClient(tc.base, "token") + if got := c.buildURL("/api/rest/users/me", nil); got != tc.want { + t.Fatalf("unexpected url: %s", got) + } + }) + } +} diff --git a/skills/mantisbt/SKILL.md b/skills/mantisbt/SKILL.md index e6947b6..55d10bc 100644 --- a/skills/mantisbt/SKILL.md +++ b/skills/mantisbt/SKILL.md @@ -22,6 +22,10 @@ export MANTISBT_URL="https://mantis.example.com" export MANTISBT_TOKEN="…" ``` +`MANTISBT_URL` may point at the MantisBT web root, or at the REST API root if +the instance exposes one, such as `https://mantis.example.com/api/rest` or +`https://mantis.example.com/api/rest/index.php`. + If they are not set, every command fails with a "missing configuration" error. You may instead pass `--url` / `--token` before the subcommand. Check connectivity with `mantisbt-cli auth whoami`.