From 2bb4af2b60c36ca45189a79cdce73d8f686f256e Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Tue, 13 Oct 2026 23:14:28 +0800 Subject: [PATCH 1/3] fix(routes): attach the public invoice middleware through the registrar RouteRegistrar::group() takes only the callback. Passing the attributes array as its first argument made Router::group() treat the array as a routes file, which fails with 'Array to string conversion' while the routes load and takes the whole API down. The Postman contract job hit it on release/v0.0.13. --- server/src/routes.php | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/server/src/routes.php b/server/src/routes.php index 924ddba..e2140ec 100644 --- a/server/src/routes.php +++ b/server/src/routes.php @@ -40,15 +40,12 @@ function ($router) { | GET /ledger/public/invoices/{public_id}/gateways | POST /ledger/public/invoices/{public_id}/pay */ - $router->prefix('public')->namespace('Public')->group( - ['middleware' => [Fleetbase\Http\Middleware\LogApiRequests::class]], - function ($router) { - $router->get('invoices/{public_id}', 'PublicInvoiceController@show'); - $router->get('invoices/{public_id}/gateways', 'PublicInvoiceController@gateways'); - $router->post('invoices/{public_id}/pay', 'PublicInvoiceController@pay'); - $router->get('refunds/{refund_id}', 'PublicInvoiceController@refund'); - } - ); + $router->prefix('public')->namespace('Public')->middleware([Fleetbase\Http\Middleware\LogApiRequests::class])->group(function ($router) { + $router->get('invoices/{public_id}', 'PublicInvoiceController@show'); + $router->get('invoices/{public_id}/gateways', 'PublicInvoiceController@gateways'); + $router->post('invoices/{public_id}/pay', 'PublicInvoiceController@pay'); + $router->get('refunds/{refund_id}', 'PublicInvoiceController@refund'); + }); /* |-------------------------------------------------------------------------- From 2424c44c63a942897c7884cca091b6d47f447b22 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Wed, 14 Oct 2026 00:05:07 +0800 Subject: [PATCH 2/3] ci: run the checks on pull requests against release and dev branches Fixes to a release branch were only verified once the release PR itself re-ran, because every workflow listed main alone. Match core-api, which already runs on dev-v* and release/v* targets. --- .github/workflows/ember.yml | 2 +- .github/workflows/postman.yml | 2 +- .github/workflows/server.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ember.yml b/.github/workflows/ember.yml index fe331a2..6ed7c53 100644 --- a/.github/workflows/ember.yml +++ b/.github/workflows/ember.yml @@ -6,7 +6,7 @@ on: tags: - 'v*' pull_request: - branches: [ main ] + branches: [ main, 'dev-v*', 'release/v*' ] env: NODE_VERSION: 22.x diff --git a/.github/workflows/postman.yml b/.github/workflows/postman.yml index 68f8296..1f9876b 100644 --- a/.github/workflows/postman.yml +++ b/.github/workflows/postman.yml @@ -13,7 +13,7 @@ on: push: branches: [main] pull_request: - branches: [main] + branches: [main, 'dev-v*', 'release/v*'] workflow_dispatch: permissions: contents: read diff --git a/.github/workflows/server.yml b/.github/workflows/server.yml index 474ddd4..183c520 100644 --- a/.github/workflows/server.yml +++ b/.github/workflows/server.yml @@ -6,7 +6,7 @@ on: tags: - 'v*' pull_request: - branches: [ main ] + branches: [ main, 'dev-v*', 'release/v*' ] jobs: build: From c25fefaafbb39c8c3ed596d587f177126afd8931 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Wed, 14 Oct 2026 00:22:13 +0800 Subject: [PATCH 3/3] test: let the fake route registrar take middleware() like Laravel's The bootstrap's RouteRegistrar only learned middleware from a group() attributes array, which is the Router form; the registrar's own middleware() call was missing, so the routes file could not be loaded in the test once it used it. --- scripts/pest-bootstrap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pest-bootstrap.php b/scripts/pest-bootstrap.php index ce82a5f..50040a5 100644 --- a/scripts/pest-bootstrap.php +++ b/scripts/pest-bootstrap.php @@ -87,7 +87,7 @@ function config(?string $key = null, mixed $default = null): mixed if (!$app->bound('router')) { if (!class_exists('Fleetbase\TestSupport\RouteRegistrar')) { - eval('namespace Fleetbase\TestSupport; class RouteRegistrar { public static array $routes = []; public static array $middleware = []; public static function reset(): void { self::$routes = []; self::$middleware = []; } public function prefix(string $prefix): self { return $this; } public function namespace(string $namespace): self { return $this; } public function group(array|\Closure $attributes, ?\Closure $callback = null): self { $previous = self::$middleware; if (is_array($attributes)) { self::$middleware = array_merge(self::$middleware, (array) ($attributes["middleware"] ?? [])); } ($callback ?? $attributes)($this); self::$middleware = $previous; return $this; } public function get(string $uri, mixed $action): self { self::$routes[] = ["GET", $uri, $action, self::$middleware]; return $this; } public function post(string $uri, mixed $action): self { self::$routes[] = ["POST", $uri, $action, self::$middleware]; return $this; } public function fleetbaseRoutes(string $resource, ?\Closure $callback = null): self { self::$routes[] = ["RESOURCE", $resource, null]; if ($callback) { $callback($this, fn (string $method): string => $resource . "Controller@" . $method); } return $this; } }'); + eval('namespace Fleetbase\TestSupport; class RouteRegistrar { public static array $routes = []; public static array $middleware = []; public static array $pending = []; public static function reset(): void { self::$routes = []; self::$middleware = []; self::$pending = []; } public function prefix(string $prefix): self { return $this; } public function namespace(string $namespace): self { return $this; } public function middleware(array|string $middleware): self { self::$pending = array_merge(self::$pending, (array) $middleware); return $this; } public function group(array|\Closure $attributes, ?\Closure $callback = null): self { $previous = self::$middleware; self::$middleware = array_merge(self::$middleware, self::$pending); self::$pending = []; if (is_array($attributes)) { self::$middleware = array_merge(self::$middleware, (array) ($attributes["middleware"] ?? [])); } ($callback ?? $attributes)($this); self::$middleware = $previous; return $this; } public function get(string $uri, mixed $action): self { self::$routes[] = ["GET", $uri, $action, self::$middleware]; return $this; } public function post(string $uri, mixed $action): self { self::$routes[] = ["POST", $uri, $action, self::$middleware]; return $this; } public function fleetbaseRoutes(string $resource, ?\Closure $callback = null): self { self::$routes[] = ["RESOURCE", $resource, null]; if ($callback) { $callback($this, fn (string $method): string => $resource . "Controller@" . $method); } return $this; } }'); } $app->singleton('router', fn () => new Fleetbase\TestSupport\RouteRegistrar());