Skip to content

Conversation

Mr-Destructive
Copy link

Addressing Issue: #224

  • Added a file upload option when the form data value has @ before the file path as convention form the curl -F option
  • Override the request content-type header dynamically with httpx
  • Segregate files and data in the formdata of request

Minor changes:

  • Refactor the data key to be default as defaultdict of list
  • We separate the key-value pairs from the file type fields if there is @ in the start of a value

Hope that is a quick and correct approach right now with the file upload part?
Let me know for any suggestions or improvements, thanks

@Mr-Destructive Mr-Destructive changed the title add file upload in request formdata Feature: Add File Upload in request form-data Mar 15, 2025
@sohaha
Copy link

sohaha commented Mar 20, 2025

When will the version be released?

@darrenburns
Copy link
Owner

Thanks for this. I'll try to review it tomorrow.

@darrenburns darrenburns requested a review from Copilot March 30, 2025 17:52
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for file uploads in form-data by detecting file paths prefixed with "@" and dynamically overriding the Content-Type header.

  • Introduces a loop that separates files from standard form-data using a defaultdict for values.
  • Updates the header formation to exclude the "content-type" header and uses a dictionary comprehension instead of a list of tuples.
Comments suppressed due to low confidence (1)

src/posting/collection.py:267

  • [nitpick] Switching from a list of tuples to a dictionary comprehension may drop duplicate headers with the same name. If multiple headers with the same name are expected, consider preserving the list format.
{ header.name: header.value for header in self.headers if header.enabled and header.name.lower() != "content-type" }


if item.value.startswith("@"):
file_path = item.value[1:]
if not Path(file_path).exists():
Copy link
Preview

Copilot AI Mar 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the file does not exist, the code adds the value to data but then continues to open the file, which can lead to a runtime error. Consider adding an else clause or a continue statement to skip the file opening when the file is missing.

Copilot uses AI. Check for mistakes.

@kkHAIKE
Copy link
Contributor

kkHAIKE commented Apr 10, 2025

I previously considered how to add file parameters in posting, with the main ideas as follows:

  1. File selector:
    • Filesystem-based textual-autocomplete
    • Or use textual-fspicker for a more polished look
  2. KeyValueEditor (form/multipart)
    1. Modify KeyValueInput to support file parameters
      • Consider how to trigger it—if using @ as the trigger, how to handle cases where @ is needed in the parameter itself
    2. How FormTable should display file paths:
      • Using the 📄 symbol looks better than @
      • Only display the file’s basename
    3. When re-entering edit mode, the full path should still be shown
  3. Add a raw_file body editor
    • Need a button to select the file
    • Display file content in read-only mode (if it’s a text file)

@darrenburns
Copy link
Owner

I think I like the idea of using textual-autocomplete for this, and triggering the autocompletion via @, similar to how we currently trigger variable autocompletion with $. I think the logic would be very similar to the checks done around $ for checking if the cursor is "within" a variable, but it'd need to mix in some of the logic from https://github.com/darrenburns/textual-autocomplete/blob/main/textual_autocomplete/_path_autocomplete.py too:

textual-autocomplete-4-path-completion__76pct_smaller.mp4

I think the above would be a good extension to what's already in this PR :)

@darrenburns
Copy link
Owner

For clarity - I do plan to merge and release this PR before adding the dropdown autocompletion.


if item.value.startswith("@"):
file_path = item.value[1:]
if not Path(file_path).exists():
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm undecided on whether the paths should be relative to the cwd or relative to the root of the collection directory. I'm concerned that if you were to share a collection, relative paths could break if they're cwd-relative.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on my experience, developers rarely share files alongside collections, so I would suggest omitting the collection directory. Additionally, the current working directory (cwd) can vary between machines. What I’ve commonly seen from other clients is the use of a general working directory like ~/home/client_x. You can set that up and place all your files there for consistency.

That's not to say, you must always place your files there, you should always be able to set an absolute path.

Comment on lines 249 to 271
[(header.name, header.value) for header in self.headers if header.enabled]
{
header.name: header.value
for header in self.headers
if header.enabled and header.name.lower() != "content-type"
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A list of tuples were being used here because you can have multiple headers with the same name and different values in a request. For example:

Accept: text/html
Accept: application/xhtml+xml
Accept: application/xml;q=0.9

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.

5 participants