fix: 414 URI Too Long — omit False-valued features from GET URL, add regression tests
This commit is contained in:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "twitter-cli"
|
name = "twitter-cli"
|
||||||
version = "0.4.1"
|
version = "0.4.2"
|
||||||
description = "A CLI for Twitter/X — feed, bookmarks, and user timeline in terminal"
|
description = "A CLI for Twitter/X — feed, bookmarks, and user timeline in terminal"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|||||||
@@ -176,6 +176,23 @@ class TestBuildGraphqlUrl:
|
|||||||
url = _build_graphql_url("x", "Op", {}, {}, {"toggle": True})
|
url = _build_graphql_url("x", "Op", {}, {}, {"toggle": True})
|
||||||
assert "fieldToggles=" in url
|
assert "fieldToggles=" in url
|
||||||
|
|
||||||
|
def test_false_features_omitted_from_url(self):
|
||||||
|
"""False-valued features should be omitted to keep URL short (avoid 414)."""
|
||||||
|
features = {"enabled_flag": True, "disabled_flag": False, "another_enabled": True}
|
||||||
|
url = _build_graphql_url("q", "Op", {}, features)
|
||||||
|
assert "enabled_flag" in url
|
||||||
|
assert "another_enabled" in url
|
||||||
|
assert "disabled_flag" not in url
|
||||||
|
|
||||||
|
def test_url_length_with_full_features(self):
|
||||||
|
"""URL with full FEATURES dict should stay under 8000 chars (server limit)."""
|
||||||
|
url = _build_graphql_url(
|
||||||
|
"abc123", "SearchTimeline",
|
||||||
|
{"rawQuery": "AI agent", "querySource": "typed_query", "product": "Latest", "count": 50},
|
||||||
|
FEATURES,
|
||||||
|
)
|
||||||
|
assert len(url) < 8000, f"URL too long: {len(url)} chars"
|
||||||
|
|
||||||
|
|
||||||
# ── _best_chrome_target ──────────────────────────────────────────────────
|
# ── _best_chrome_target ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@@ -164,12 +164,18 @@ def _url_fetch(url, headers=None):
|
|||||||
|
|
||||||
def _build_graphql_url(query_id, operation_name, variables, features, field_toggles=None):
|
def _build_graphql_url(query_id, operation_name, variables, features, field_toggles=None):
|
||||||
# type: (str, str, Dict[str, Any], Dict[str, Any], Optional[Dict[str, Any]]) -> str
|
# type: (str, str, Dict[str, Any], Dict[str, Any], Optional[Dict[str, Any]]) -> str
|
||||||
"""Build GraphQL GET URL with encoded variables/features/fieldToggles."""
|
"""Build GraphQL GET URL with encoded variables/features/fieldToggles.
|
||||||
|
|
||||||
|
Only includes True-valued feature flags in the URL to avoid 414 URI Too Long.
|
||||||
|
Twitter's API defaults missing features to False.
|
||||||
|
"""
|
||||||
|
# Compact features: omit False values to keep URL under server limits
|
||||||
|
compact_features = {k: v for k, v in features.items() if v is not False}
|
||||||
url = "https://x.com/i/api/graphql/%s/%s?variables=%s&features=%s" % (
|
url = "https://x.com/i/api/graphql/%s/%s?variables=%s&features=%s" % (
|
||||||
query_id,
|
query_id,
|
||||||
operation_name,
|
operation_name,
|
||||||
urllib.parse.quote(json.dumps(variables, separators=(",", ":"))),
|
urllib.parse.quote(json.dumps(variables, separators=(",", ":"))),
|
||||||
urllib.parse.quote(json.dumps(features, separators=(",", ":"))),
|
urllib.parse.quote(json.dumps(compact_features, separators=(",", ":"))),
|
||||||
)
|
)
|
||||||
if field_toggles:
|
if field_toggles:
|
||||||
url += "&fieldToggles=%s" % urllib.parse.quote(
|
url += "&fieldToggles=%s" % urllib.parse.quote(
|
||||||
|
|||||||
2
uv.lock
generated
2
uv.lock
generated
@@ -950,7 +950,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "twitter-cli"
|
name = "twitter-cli"
|
||||||
version = "0.4.0"
|
version = "0.4.1"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "beautifulsoup4" },
|
{ name = "beautifulsoup4" },
|
||||||
|
|||||||
Reference in New Issue
Block a user