$this->markdown('docs/api-guide.md'), // A second page rather than a section of the guide: the guide // is written for someone building against the API, this is // written for someone wiring up a Zap and reading nothing else. 'zapier_html' => $this->markdown('docs/api-zapier.md'), 'endpoints' => $this->endpoints(), 'spec_url' => route('api.openapi'), 'version' => $this->spec()['info']['version'] ?? null, ]); } /** * @return array */ private function spec(): array { $path = base_path(OpenApiController::PATH); if (! is_file($path)) { return []; } return json_decode((string) file_get_contents($path), true) ?: []; } /** * Flattened operation list for the summary table: one row per method * and path, with the abilities pulled back out of the description * where describeOpenApi() put them. * * @return list> */ private function endpoints(): array { $rows = []; foreach ($this->spec()['paths'] ?? [] as $path => $operations) { foreach ($operations as $method => $operation) { if (! in_array($method, ['get', 'post', 'put', 'patch', 'delete'], true)) { continue; } preg_match_all('/`([a-z_]+)`/', $this->abilitySentence($operation), $matches); $rows[] = [ 'method' => strtoupper($method), 'path' => '/api/v1'.$path, 'summary' => $operation['summary'] ?? '', 'abilities' => $matches[1], ]; } } usort($rows, fn (array $a, array $b): int => [$a['path'], $a['method']] <=> [$b['path'], $b['method']]); return $rows; } /** * @param array $operation */ private function abilitySentence(array $operation): string { $description = is_string($operation['description'] ?? null) ? $operation['description'] : ''; $position = strpos($description, 'Requires a token with'); return $position === false ? '' : substr($description, $position); } /** * @param string $file repository-relative path to a markdown file * shipped with the application */ private function markdown(string $file): string { $path = base_path($file); if (! is_file($path)) { return ''; } // A deliberately small extension set. These are files shipped with // the application, not user input — but rendering them with the // narrowest converter that does the job keeps it that way even if // someone later points this at something less trustworthy. $environment = new Environment([ 'html_input' => 'escape', 'allow_unsafe_links' => false, ]); $environment->addExtension(new CommonMarkCoreExtension); $environment->addExtension(new TableExtension); return (string) (new MarkdownConverter($environment))->convert((string) file_get_contents($path)); } }