mergeRoute($base['paths'], $route); } return $base; } private function mergeRoute(array &$paths, Route $route): void { if (!$this->isDocumentableRoute($route)) { return; } $uriRaw = ltrim($route->uri(), '/'); if (str_starts_with($uriRaw, 'api/')) { $uri = '/' . $uriRaw; } elseif (str_starts_with($uriRaw, 'v1/')) { $uri = '/api/' . $uriRaw; } elseif ($uriRaw === 'v1') { $uri = '/api/v1'; } else { return; } if (!str_starts_with($uri, '/api/v1')) { return; } $methods = array_diff($route->methods(), ['HEAD']); if (empty($methods)) { return; } $tag = $this->resolveTag($uri); foreach ($methods as $method) { $lower = strtolower($method); if (!isset($paths[$uri][$lower])) { $paths[$uri][$lower] = [ 'tags' => [$tag], 'summary' => $this->summaryFromAction($route), 'responses' => [ '200' => [ 'description' => 'OK', ], ], ]; } } } private function isDocumentableRoute(Route $route): bool { $uri = ltrim($route->uri(), '/'); if (!str_starts_with($uri, 'api/v1') && !str_starts_with($uri, 'v1/')) { return false; } $action = $route->getActionName(); if (!is_string($action)) { return false; } if (str_contains($action, 'DocsController')) { return false; } if ($action === 'Closure') { return true; } return true; } private function resolveTag(string $uri): string { $trimmed = trim(str_replace('/api/v1', '', $uri), '/'); if ($trimmed === '') { return 'api'; } $segments = explode('/', $trimmed); $segment = $segments[0] ?: 'api'; $secondary = $segments[1] ?? null; $normalized = Str::title(str_replace('-', ' ', $segment)); $overrides = [ 'Whatsapp' => 'WhatsApp', ]; if ($segment === 'finance' && $secondary === 'refunds') { return 'Refunds'; } return $overrides[$normalized] ?? $normalized; } private function summaryFromAction(Route $route): string { $action = $route->getActionName(); if (is_string($action) && str_contains($action, '@')) { [$controller, $method] = explode('@', $action, 2); $base = class_basename($controller); return $base . '::' . $method; } if (is_string($action)) { return $action; } return 'Endpoint'; } }