Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion cmd/kcl/commands/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Supported conversion modes:
- openapi: convert OpenAPI spec to KCL schema
- crd: convert Kubernetes CRD to KCL schema
- auto: automatically detect the input format

Input can be a local file path or an HTTP/HTTPS URL.
`
importExample = ` # Generate KCL models from OpenAPI spec
kcl import -m openapi swagger.json
Expand All @@ -43,7 +45,13 @@ Supported conversion modes:
kcl import -m terraformschema schema.json

# Generate KCL models from Go structs
kcl import -m gostruct schema.go`
kcl import -m gostruct schema.go

# Generate KCL models from a remote JSON URL
kcl import -m json https://api.github.com/meta

# Generate KCL models from a raw GitHub YAML file
kcl import https://raw.githubusercontent.com/kcl-lang/cli/main/examples/settings/settings.yaml`
)

// NewImportCmd returns the import command.
Expand Down
55 changes: 55 additions & 0 deletions pkg/fs/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,65 @@ package fs
import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
)

// IsURL checks if the given path is an HTTP or HTTPS URL.
func IsURL(path string) bool {
return strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://")
}

// GenTempFileFromURL fetches content from a URL and saves it to a temp file.
// It returns the path to the temp file or an error if the fetch fails.
// The caller is responsible for removing the temp file after use.
func GenTempFileFromURL(urlStr string) (string, error) {
// Parse the URL to extract the file extension
parsedURL, err := url.Parse(urlStr)
if err != nil {
return "", fmt.Errorf("invalid URL %q: %w", urlStr, err)
}

// Extract file extension from URL path for temp file naming
ext := filepath.Ext(parsedURL.Path)
if ext == "" {
ext = ".tmp"
}

// Create temp file with appropriate extension
tempFile, err := os.CreateTemp("", "kcl-import-*"+ext)
if err != nil {
return "", fmt.Errorf("failed to create temp file: %w", err)
}
defer tempFile.Close()

// Fetch content from URL
resp, err := http.Get(urlStr)
if err != nil {
os.Remove(tempFile.Name())
return "", fmt.Errorf("failed to fetch URL %q: %w", urlStr, err)
}
defer resp.Body.Close()

// Check for successful response
if resp.StatusCode != http.StatusOK {
os.Remove(tempFile.Name())
return "", fmt.Errorf("failed to fetch URL %q: HTTP %d %s", urlStr, resp.StatusCode, resp.Status)
}

// Copy response body to temp file
_, err = io.Copy(tempFile, resp.Body)
if err != nil {
os.Remove(tempFile.Name())
return "", fmt.Errorf("failed to save content from URL %q: %w", urlStr, err)
}

return tempFile.Name(), nil
}

func GenTempFileFromStdin() (string, error) {
tempFile, err := os.CreateTemp("", "stdin-*.k")
if err != nil {
Expand Down
31 changes: 30 additions & 1 deletion pkg/options/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,36 @@ func NewImportOptions() *ImportOptions {
func (o *ImportOptions) Run() error {
opts := &gen.GenKclOptions{}
mode := strings.ToLower(o.Mode)
files, err := fs.ExpandInputFiles(o.Files, o.Recursive)

// Process input files, fetching URLs to temp files if needed
processedFiles := make([]string, 0, len(o.Files))
tempFiles := []string{}

for _, f := range o.Files {
if fs.IsURL(f) {
// Fetch URL content to temp file
tempPath, err := fs.GenTempFileFromURL(f)
if err != nil {
for _, tf := range tempFiles {
os.Remove(tf)
}
return err
}
tempFiles = append(tempFiles, tempPath)
processedFiles = append(processedFiles, tempPath)
} else {
processedFiles = append(processedFiles, f)
}
}

// Ensure temp files are cleaned up when function returns
defer func() {
for _, tf := range tempFiles {
os.Remove(tf)
}
}()

files, err := fs.ExpandInputFiles(processedFiles, o.Recursive)
if err != nil {
return err
}
Expand Down