Skip to content

Add Postman Collection v2.1 export command#3

Merged
vedssharma merged 1 commit intomainfrom
claude/cli-postman-export-nIoon
Mar 16, 2026
Merged

Add Postman Collection v2.1 export command#3
vedssharma merged 1 commit intomainfrom
claude/cli-postman-export-nIoon

Conversation

@vedssharma
Copy link
Copy Markdown
Owner

Adds apicli export postman to convert stored requests to a Postman
Collection JSON file that can be imported directly into Postman.

Supports three modes:

  • Export all collections (default)
  • Export a specific collection: --collection
  • Export request history: --history

Output goes to stdout by default; use --output to write to a file.
URLs are fully parsed into Postman's structured format (protocol, host,
path segments, query params). Request bodies are auto-detected as JSON.

https://claude.ai/code/session_01PyL8EW4HoiuaCszqviyfGt

Adds `apicli export postman` to convert stored requests to a Postman
Collection JSON file that can be imported directly into Postman.

Supports three modes:
- Export all collections (default)
- Export a specific collection: --collection <name>
- Export request history: --history

Output goes to stdout by default; use --output <file> to write to a file.
URLs are fully parsed into Postman's structured format (protocol, host,
path segments, query params). Request bodies are auto-detected as JSON.

https://claude.ai/code/session_01PyL8EW4HoiuaCszqviyfGt
@vedssharma vedssharma merged commit 79536eb into main Mar 16, 2026
1 check passed
@vedssharma vedssharma deleted the claude/cli-postman-export-nIoon branch March 16, 2026 03:19
@claude
Copy link
Copy Markdown

claude Bot commented Mar 16, 2026

PR Review: Add Postman Collection v2.1 export command

Overall the implementation is clean and well-structured. Good separation of concerns across the builder functions, proper error handling with os.Exit(1), and correct use of 0600 permissions for the output file. A few issues worth addressing before merging:


Bugs / Correctness

1. --collection and --history flags are not mutually exclusive

Both flags can be passed simultaneously (--collection foo --history). The if/else if/else chain silently picks --history first. Either make them mutually exclusive or document the precedence:

if collectionName != "" && exportHistory {
    format.PrintError("--collection and --history are mutually exclusive")
    os.Exit(1)
}

2. isJSON is a syntactic heuristic, not validation

Strings like {invalid json here} pass the check and will be tagged language: json in the export, which may confuse Postman's editor. Consider using json.Valid():

func isJSON(s string) bool {
    return json.Valid([]byte(strings.TrimSpace(s)))
}

3. All collections are flattened in buildPostmanFromAllCollections

When exporting multiple collections, all requests are merged into a single flat list — collection names are lost. Postman v2.1 supports folder items ("item" can contain nested "item" arrays). This would preserve structure:

type postmanItem struct {
    Name    string         `json:"name"`
    Request *postmanRequest `json:"request,omitempty"`  // nil for folders
    Items   []postmanItem   `json:"item,omitempty"`      // non-nil for folders
}

Then each collection becomes a folder item containing its requests.


Code Quality

4. Use Bool instead of BoolP for flags without a shorthand

BoolP("history", "", false, ...) passes an empty string as the shorthand, which is unconventional. Use Bool for flags with no shorthand:

postmanCmd.Flags().Bool("history", false, "Export request history instead of collections")

5. Non-deterministic header ordering

Iterating over map[string]string in buildPostmanRequest produces different header orderings on each run. For reproducible exports, sort the keys:

keys := make([]string, 0, len(headers))
for k := range headers { keys = append(keys, k) }
sort.Strings(keys)
for _, k := range keys {
    pHeaders = append(pHeaders, postmanHeader{Key: k, Value: headers[k]})
}

6. buildPostmanFromAllCollections name-selection logic is surprising

The logic that picks the single collection's name when there's exactly one collection (if len(collections.Collections) == 1) requires a loop to extract it. A cleaner approach would be to always use "apicli Export" or let the caller name it, since this is the "all collections" path.


Test Coverage

No tests are included. At minimum, consider adding unit tests for:

  • parsePostmanURL — especially edge cases: URLs with no path, query params, no scheme, unparseable input
  • isJSON — valid JSON, invalid JSON, empty string, JSON arrays
  • buildPostmanFromCollection with a nil collection result
  • The mutual-exclusion flag validation once added

Minor

  • The Schema URL string "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" is repeated in three places — extract it as a constant.
  • items := make([]postmanItem, 0) in buildPostmanFromAllCollections could preallocate by counting total requests across collections, though this is trivial at CLI scale.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants