-
-
Notifications
You must be signed in to change notification settings - Fork 202
Feature: Add File Upload in request form-data #231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
from __future__ import annotations | ||
from functools import total_ordering | ||
from collections import defaultdict | ||
from urllib.parse import urlparse, parse_qsl, urlencode, urlunparse | ||
from pathlib import Path | ||
from string import Template | ||
|
@@ -10,7 +11,6 @@ | |
import os | ||
from textual import log | ||
from posting.auth import HttpxBearerTokenAuth | ||
from posting.tuple_to_multidict import tuples_to_dict | ||
from posting.variables import SubstitutionError | ||
from posting.version import VERSION | ||
from posting.yaml import dump, load, Loader | ||
|
@@ -117,10 +117,28 @@ def to_httpx_args(self) -> dict[str, Any]: | |
if self.content: | ||
httpx_args["content"] = self.content | ||
if self.form_data: | ||
# Ensure we don't delete duplicate keys | ||
httpx_args["data"] = tuples_to_dict( | ||
[(item.name, item.value) for item in self.form_data if item.enabled] | ||
) | ||
files = [] | ||
data = defaultdict(list) | ||
|
||
for item in self.form_data: | ||
if not item.enabled: | ||
continue | ||
|
||
if item.value.startswith("@"): | ||
file_path = item.value[1:] | ||
if not Path(file_path).exists(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
log.warning(f"File {file_path} does not exist") | ||
data[item.name].append(item.value) | ||
|
||
file_obj = open(file_path, "rb") | ||
file_name = Path(file_path).name | ||
files.append((item.name, (file_name, file_obj, "application/octet-stream"))) | ||
else: | ||
data[item.name].append(item.value) | ||
|
||
if files: | ||
httpx_args["files"] = files | ||
httpx_args["data"] = data | ||
return httpx_args | ||
|
||
|
||
|
@@ -246,12 +264,18 @@ def apply_template(self, variables: dict[str, Any]) -> None: | |
def to_httpx(self, client: httpx.AsyncClient) -> httpx.Request: | ||
"""Convert the request model to an httpx request.""" | ||
headers = httpx.Headers( | ||
[(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" | ||
} | ||
|
||
) | ||
|
||
httpx_args = self.body.to_httpx_args() if self.body else {} | ||
|
||
return client.build_request( | ||
method=self.method, | ||
url=self.url, | ||
**(self.body.to_httpx_args() if self.body else {}), | ||
headers=headers, | ||
params=httpx.QueryParams( | ||
[(param.name, param.value) for param in self.params if param.enabled] | ||
|
@@ -263,6 +287,7 @@ def to_httpx(self, client: httpx.AsyncClient) -> httpx.Request: | |
if cookie.enabled | ||
] | ||
), | ||
**httpx_args | ||
) | ||
|
||
def save_to_disk(self, path: Path) -> None: | ||
|
There was a problem hiding this comment.
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.