Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,4 @@ internal/appinfo/update_config.json

*.asc
dist/
.gocache
125 changes: 60 additions & 65 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,6 @@ type RequestOptions struct {
PostResponseScript string `json:"postResponseScript,omitempty"`
}

type OpenAPIImportCollectionResult struct {
Warnings []string `json:"warnings"`
BasePath string `json:"basePath"`
Servers []string `json:"servers"`
}

func (a *App) GetAppInfo() appinfo.AppInfo {
return appinfo.GetAppInfo(wailsJSON)
}
Expand Down Expand Up @@ -446,44 +440,33 @@ func (a *App) SetSelectedEnvironment(name string) error {

// Collection Management Methods

// ImportPostmanCollection imports a Postman v2.1 collection file into Solo.
func (a *App) ImportPostmanCollection(path string) error {
imp := importer.NewPostmanImporter()

coll, err := imp.Import(path)
if err != nil {
return fmt.Errorf("failed to import collection: %w", err)
}

// Save the imported collection using the existing manager.
// UpdateCollection will write the entire JSON object to disk using its Name.
if err := a.collectionManager.UpdateCollection(*coll); err != nil {
return fmt.Errorf("failed to save imported collection: %w", err)
// ImportPostmanCollections imports Postman v2.1 collection files into Solo.
func (a *App) ImportPostmanCollections(paths []string, overwriteExisting bool) (collection.BatchImportResult, error) {
if a.collectionManager == nil {
return collection.BatchImportResult{}, fmt.Errorf("collection manager not initialized")
}

return nil
imp := importer.NewPostmanImporter()
return a.collectionManager.ImportBatch(paths, overwriteExisting, func(path string) (*collection.Collection, []string, error) {
coll, err := imp.Import(path)
return coll, nil, err
})
}

// ImportOpenAPICollection imports an OpenAPI 3.x or Swagger 2.x collection
// (JSON or YAML) into Solo.
// Returns warning messages and source metadata used by the frontend to build baseUrl.
func (a *App) ImportOpenAPICollection(path string) (OpenAPIImportCollectionResult, error) {
imp := importer.NewOpenAPIImporter()

result, err := imp.Import(path)
if err != nil {
return OpenAPIImportCollectionResult{}, fmt.Errorf("failed to import OpenAPI collection: %w", err)
}

if err := a.collectionManager.UpdateCollection(*result.Collection); err != nil {
return OpenAPIImportCollectionResult{}, fmt.Errorf("failed to save imported OpenAPI collection: %w", err)
// ImportOpenAPICollections imports OpenAPI 3.x or Swagger 2.x collection files into Solo.
func (a *App) ImportOpenAPICollections(paths []string, overwriteExisting bool) (collection.BatchImportResult, error) {
if a.collectionManager == nil {
return collection.BatchImportResult{}, fmt.Errorf("collection manager not initialized")
}

return OpenAPIImportCollectionResult{
Warnings: result.Warnings,
BasePath: result.BasePath,
Servers: result.Servers,
}, nil
imp := importer.NewOpenAPIImporter()
return a.collectionManager.ImportBatch(paths, overwriteExisting, func(path string) (*collection.Collection, []string, error) {
result, err := imp.Import(path)
if err != nil {
return nil, nil, err
}
return result.Collection, result.Warnings, nil
})
}

// ImportCurlRequest parses a cURL command string and adds the resulting request
Expand Down Expand Up @@ -586,34 +569,34 @@ func (a *App) ExportEnvironment(environmentName string) error {
return os.WriteFile(path, data, 0644)
}

// ImportSoloCollection imports a Solo-native collection JSON file.
// If overwrite is false and a collection with the same name already exists,
// returns an error of the form "collection <name> already exists".
func (a *App) ImportSoloCollection(path string, overwrite bool) error {
func (a *App) loadSoloCollectionForImport(path string) (*collection.Collection, []string, error) {
data, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("failed to read file: %w", err)
return nil, nil, fmt.Errorf("failed to read file: %w", err)
}

var coll collection.Collection
if err := json.Unmarshal(data, &coll); err != nil {
return fmt.Errorf("invalid %s collection file: %w", tools.APP_NAME, err)
return nil, nil, fmt.Errorf("invalid %s collection file: %w", tools.APP_NAME, err)
}
if coll.Name == "" {
return fmt.Errorf("collection file has no name field")
return nil, nil, fmt.Errorf("collection file has no name field")
}

if !overwrite {
existing, _ := a.collectionManager.LoadCollection(coll.Name)
if existing != nil {
return fmt.Errorf("collection %s already exists", coll.Name)
}
return &coll, nil, nil
}

// ImportSoloCollections imports Solo-native collection JSON files into Solo.
func (a *App) ImportSoloCollections(paths []string, overwriteExisting bool) (collection.BatchImportResult, error) {
if a.collectionManager == nil {
return collection.BatchImportResult{}, fmt.Errorf("collection manager not initialized")
}

if err := a.collectionManager.UpdateCollection(coll); err != nil {
return fmt.Errorf("failed to save collection: %w", err)
importOne := func(path string) (*collection.Collection, []string, error) {
return a.loadSoloCollectionForImport(path)
}
return nil

return a.collectionManager.ImportBatch(paths, overwriteExisting, importOne)
}

// ImportSoloEnvironment imports a Solo-native environment JSON file.
Expand Down Expand Up @@ -727,20 +710,17 @@ func (a *App) ExportLogsZip() (bool, error) {
return true, nil
}

// ImportBrunoCollection imports a Bruno collection from a directory.
func (a *App) ImportBrunoCollection(path string) error {
imp := importer.NewBrunoImporter()

coll, err := imp.Import(path)
if err != nil {
return fmt.Errorf("failed to import Bruno collection: %w", err)
}

if err := a.collectionManager.UpdateCollection(*coll); err != nil {
return fmt.Errorf("failed to save imported Bruno collection: %w", err)
// ImportBrunoCollections imports Bruno collection directories into Solo.
func (a *App) ImportBrunoCollections(paths []string, overwriteExisting bool) (collection.BatchImportResult, error) {
if a.collectionManager == nil {
return collection.BatchImportResult{}, fmt.Errorf("collection manager not initialized")
}

return nil
imp := importer.NewBrunoImporter()
return a.collectionManager.ImportBatch(paths, overwriteExisting, func(path string) (*collection.Collection, []string, error) {
coll, err := imp.Import(path)
return coll, nil, err
})
}

// ImportPostmanEnvironment imports a Postman environment JSON file.
Expand Down Expand Up @@ -1292,6 +1272,21 @@ func (a *App) SelectFile(title, patterns, displayName string) (string, error) {
})
}

// SelectFiles opens a native file dialog to select multiple files.
// It takes a title for the dialog and a pattern for file filtering (e.g., "*.pem;*.crt").
func (a *App) SelectFiles(title, patterns, displayName string) ([]string, error) {
slog.Debug("Opening multiple files dialog", "title", title, "patterns", patterns)
return runtime.OpenMultipleFilesDialog(a.ctx, runtime.OpenDialogOptions{
Title: title,
Filters: []runtime.FileFilter{
{
DisplayName: displayName,
Pattern: patterns, // e.g., "*.pem;*.crt;*.key"
},
},
})
}

// ── Git helpers ──────────────────────────────────────────────────────────────

// resolveGitCollectionDir resolves a collectionId to its local git repo dir
Expand Down
29 changes: 29 additions & 0 deletions docs/releases/0.3.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Solo release 0.3.0

## Highlights

## ✨ New Features

- Add batch collection import for Postman, Bruno, OpenAPI/Swagger, and Solo-native collection formats ([#156](https://github.com/raml-dev/solo/pull/156)).
- Add multi-file picker support for file-based collection imports. Bruno remains folder-based through the picker, with multi-directory import supported through drag-and-drop ([#156](https://github.com/raml-dev/solo/pull/156)).
- Add per-source batch import results with imported, failed, warning, and conflict states ([#156](https://github.com/raml-dev/solo/pull/156)).
- Add a conflict review flow for batch imports so users can choose which existing collections to overwrite instead of overwriting implicitly ([#156](https://github.com/raml-dev/solo/pull/156)).
- Add `SelectFiles` to the Wails API for native multi-file selection ([#156](https://github.com/raml-dev/solo/pull/156)).

## 🐞 Bug fixes

- Fix Bruno collection import for `params:query` sections ([#156](https://github.com/raml-dev/solo/pull/156)).
- Fix Bruno path parameter handling by substituting `params:path` values into request URLs ([#156](https://github.com/raml-dev/solo/pull/156)).
- Fix Bruno query parameter duplication when a parameter is already present in the URL ([#156](https://github.com/raml-dev/solo/pull/156)).
- Fix the environment manager modal layout so it no longer opens fullscreen and matches the Settings modal sizing ([#156](https://github.com/raml-dev/solo/pull/156)).
- Ignore local `.gocache` directories in Git.

## ⬆️ Dependency updates

- Updated `vite` from `8.0.5` to `8.0.16`.
- Updated `dompurify` from `3.4.0` to `3.4.11`.
- Updated Go indirect dependencies:
- `golang.org/x/crypto` from `0.51.0` to `0.55.0`
- `golang.org/x/net` from `0.54.0` to `0.58.0`
- `golang.org/x/sys` from `0.44.0` to `0.47.0`
- `golang.org/x/text` from `0.37.0` to `0.41.0`
40 changes: 20 additions & 20 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/package.json.md5
Original file line number Diff line number Diff line change
@@ -1 +1 @@
a0d58cdeee5fe9a475016d1d05aad5c9
1b75c4f1294551a5ad3586f8db6f4c74
Loading