Skip to content
Open
Changes from 1 commit
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
39 changes: 32 additions & 7 deletions src/posting/collection.py
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
Expand All @@ -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
Expand Down Expand Up @@ -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():
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.

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.

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


Expand Down Expand Up @@ -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"
}
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

)

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]
Expand All @@ -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:
Expand Down
Loading