feat(sdk): add OpenAPI spec detection
Detects OpenAPI spec via: 1. Configured spec path 2. Common paths (api/openapi.yaml, openapi.yaml, etc.) 3. Laravel Scramble (stub for now) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
64a5b0e605
commit
c2b1e5dc61
2 changed files with 127 additions and 0 deletions
78
pkg/sdk/detect.go
Normal file
78
pkg/sdk/detect.go
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// commonSpecPaths are checked in order when no spec is configured.
|
||||
var commonSpecPaths = []string{
|
||||
"api/openapi.yaml",
|
||||
"api/openapi.json",
|
||||
"openapi.yaml",
|
||||
"openapi.json",
|
||||
"docs/api.yaml",
|
||||
"docs/api.json",
|
||||
"swagger.yaml",
|
||||
"swagger.json",
|
||||
}
|
||||
|
||||
// DetectSpec finds the OpenAPI spec file.
|
||||
// Priority: config path -> common paths -> Laravel Scramble.
|
||||
func (s *SDK) DetectSpec() (string, error) {
|
||||
// 1. Check configured path
|
||||
if s.config.Spec != "" {
|
||||
specPath := filepath.Join(s.projectDir, s.config.Spec)
|
||||
if _, err := os.Stat(specPath); err == nil {
|
||||
return specPath, nil
|
||||
}
|
||||
return "", fmt.Errorf("sdk.DetectSpec: configured spec not found: %s", s.config.Spec)
|
||||
}
|
||||
|
||||
// 2. Check common paths
|
||||
for _, p := range commonSpecPaths {
|
||||
specPath := filepath.Join(s.projectDir, p)
|
||||
if _, err := os.Stat(specPath); err == nil {
|
||||
return specPath, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Try Laravel Scramble detection
|
||||
specPath, err := s.detectScramble()
|
||||
if err == nil {
|
||||
return specPath, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("sdk.DetectSpec: no OpenAPI spec found (checked config, common paths, Scramble)")
|
||||
}
|
||||
|
||||
// detectScramble checks for Laravel Scramble and exports the spec.
|
||||
func (s *SDK) detectScramble() (string, error) {
|
||||
composerPath := filepath.Join(s.projectDir, "composer.json")
|
||||
if _, err := os.Stat(composerPath); err != nil {
|
||||
return "", fmt.Errorf("no composer.json")
|
||||
}
|
||||
|
||||
// Check for scramble in composer.json
|
||||
data, err := os.ReadFile(composerPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Simple check for scramble package
|
||||
if !containsScramble(data) {
|
||||
return "", fmt.Errorf("scramble not found in composer.json")
|
||||
}
|
||||
|
||||
// TODO: Run php artisan scramble:export
|
||||
return "", fmt.Errorf("scramble export not implemented")
|
||||
}
|
||||
|
||||
// containsScramble checks if composer.json includes scramble.
|
||||
func containsScramble(data []byte) bool {
|
||||
content := string(data)
|
||||
return strings.Contains(content, "dedoc/scramble") ||
|
||||
strings.Contains(content, "\"scramble\"")
|
||||
}
|
||||
49
pkg/sdk/detect_test.go
Normal file
49
pkg/sdk/detect_test.go
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDetectSpec_Good_ConfigPath(t *testing.T) {
|
||||
// Create temp directory with spec at configured path
|
||||
tmpDir := t.TempDir()
|
||||
specPath := filepath.Join(tmpDir, "api", "spec.yaml")
|
||||
os.MkdirAll(filepath.Dir(specPath), 0755)
|
||||
os.WriteFile(specPath, []byte("openapi: 3.0.0"), 0644)
|
||||
|
||||
sdk := New(tmpDir, &Config{Spec: "api/spec.yaml"})
|
||||
got, err := sdk.DetectSpec()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got != specPath {
|
||||
t.Errorf("got %q, want %q", got, specPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectSpec_Good_CommonPath(t *testing.T) {
|
||||
// Create temp directory with spec at common path
|
||||
tmpDir := t.TempDir()
|
||||
specPath := filepath.Join(tmpDir, "openapi.yaml")
|
||||
os.WriteFile(specPath, []byte("openapi: 3.0.0"), 0644)
|
||||
|
||||
sdk := New(tmpDir, nil)
|
||||
got, err := sdk.DetectSpec()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got != specPath {
|
||||
t.Errorf("got %q, want %q", got, specPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectSpec_Bad_NotFound(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
sdk := New(tmpDir, nil)
|
||||
_, err := sdk.DetectSpec()
|
||||
if err == nil {
|
||||
t.Fatal("expected error for missing spec")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue