justoneapi 3.0.81__tar.gz → 3.0.82__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {justoneapi-3.0.81/justoneapi.egg-info → justoneapi-3.0.82}/PKG-INFO +1 -1
- justoneapi-3.0.82/justoneapi/_version.py +1 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/xiaohongshu.py +23 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82/justoneapi.egg-info}/PKG-INFO +1 -1
- {justoneapi-3.0.81 → justoneapi-3.0.82}/tests/test_codegen.py +111 -0
- justoneapi-3.0.81/justoneapi/_version.py +0 -1
- {justoneapi-3.0.81 → justoneapi-3.0.82}/LICENSE +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/MANIFEST.in +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/README.md +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/__init__.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/_exceptions.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/_resource.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/_response.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/_transport.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/client.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/config.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/__init__.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/models.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/__init__.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/aliexpress.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/amazon.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/beike.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/bilibili.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/dewu.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/douban.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/douyin.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/douyin_ec.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/douyin_xingtu.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/facebook.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/field_1688.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/imdb.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/instagram.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/jd.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/kuaishou.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/linkedin.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/llm.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/pixabay.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/qq_huxuan.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/reddit.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/search.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/shopee.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/taobao.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/temu.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/tiktok.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/tiktok_shop.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/toutiao.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/twitter.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/vcg.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/weibo.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/weixin.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/weixin_channels.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/xianyu.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/xiaohongshu_ec.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/xiaohongshu_pgy.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/youku.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/youtube.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/zcool.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/generated/resources/zhihu.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi/log.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi.egg-info/SOURCES.txt +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi.egg-info/dependency_links.txt +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi.egg-info/requires.txt +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/justoneapi.egg-info/top_level.txt +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/pyproject.toml +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/setup.cfg +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/tests/test_bump_version.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/tests/test_generated_surface.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/tests/test_integration_smoke.py +0 -0
- {justoneapi-3.0.81 → justoneapi-3.0.82}/tests/test_runtime.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "3.0.82"
|
|
@@ -337,6 +337,29 @@ class XiaohongshuResource(BaseResource):
|
|
|
337
337
|
},
|
|
338
338
|
)
|
|
339
339
|
|
|
340
|
+
def get_user_faved_note_list_v1(
|
|
341
|
+
self,
|
|
342
|
+
*,
|
|
343
|
+
user_id: str,
|
|
344
|
+
last_cursor: str | None = "",
|
|
345
|
+
) -> ApiResponse[Any]:
|
|
346
|
+
"""
|
|
347
|
+
User Favorited Notes
|
|
348
|
+
|
|
349
|
+
Retrieves a Xiaohongshu (RedNote) user's favorited notes by user ID with cursor pagination. Use it to review a user's saved content and research content interests.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
user_id: Unique user identifier on Xiaohongshu.
|
|
353
|
+
last_cursor: For the next page, pass the cursor from the previous response. Omit or pass an empty value for the first page.
|
|
354
|
+
"""
|
|
355
|
+
return self._get(
|
|
356
|
+
"/api/xiaohongshu/get-user-faved-note-list/v1",
|
|
357
|
+
{
|
|
358
|
+
"userId": user_id,
|
|
359
|
+
"lastCursor": last_cursor,
|
|
360
|
+
},
|
|
361
|
+
)
|
|
362
|
+
|
|
340
363
|
def get_note_detail_v1(
|
|
341
364
|
self,
|
|
342
365
|
*,
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import json
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
|
|
6
|
+
import httpx
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from tools import sdk_codegen
|
|
5
10
|
from tools.sdk_codegen import (
|
|
6
11
|
build_diff_summary,
|
|
7
12
|
collect_resources,
|
|
13
|
+
fetch_openapi,
|
|
8
14
|
load_json,
|
|
9
15
|
load_overrides,
|
|
10
16
|
normalize_spec,
|
|
@@ -14,6 +20,111 @@ RAW_SPEC_PATH = Path("openapi/public-api.json")
|
|
|
14
20
|
OVERRIDES_PATH = Path("openapi/sdk_overrides.yaml")
|
|
15
21
|
|
|
16
22
|
|
|
23
|
+
@pytest.fixture
|
|
24
|
+
def mock_openapi_fetch(monkeypatch):
|
|
25
|
+
outcomes = []
|
|
26
|
+
requests = []
|
|
27
|
+
delays = []
|
|
28
|
+
|
|
29
|
+
def handle(request):
|
|
30
|
+
requests.append(request)
|
|
31
|
+
outcome = outcomes.pop(0)
|
|
32
|
+
if isinstance(outcome, Exception):
|
|
33
|
+
raise outcome
|
|
34
|
+
return outcome
|
|
35
|
+
|
|
36
|
+
client_type = httpx.Client
|
|
37
|
+
monkeypatch.setattr(
|
|
38
|
+
sdk_codegen.httpx,
|
|
39
|
+
"Client",
|
|
40
|
+
lambda **kwargs: client_type(transport=httpx.MockTransport(handle), **kwargs),
|
|
41
|
+
)
|
|
42
|
+
monkeypatch.setattr(sdk_codegen.time, "sleep", delays.append)
|
|
43
|
+
return outcomes, requests, delays
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@pytest.mark.parametrize("timeout_type", [httpx.ConnectTimeout, httpx.ReadTimeout])
|
|
47
|
+
@pytest.mark.parametrize("timeouts_before_success", [0, 1, 2])
|
|
48
|
+
def test_fetch_openapi_recovers_from_temporary_timeouts(
|
|
49
|
+
mock_openapi_fetch, timeout_type, timeouts_before_success
|
|
50
|
+
):
|
|
51
|
+
outcomes, requests, delays = mock_openapi_fetch
|
|
52
|
+
spec = {"openapi": "3.0.3", "paths": {}}
|
|
53
|
+
outcomes.extend(
|
|
54
|
+
timeout_type("Temporary timeout") for _ in range(timeouts_before_success)
|
|
55
|
+
)
|
|
56
|
+
outcomes.append(httpx.Response(200, json=spec))
|
|
57
|
+
|
|
58
|
+
assert (
|
|
59
|
+
fetch_openapi("https://example.test/openapi", "test-user", "test-pass") == spec
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
assert len(requests) == timeouts_before_success + 1
|
|
63
|
+
assert delays == [2, 4][:timeouts_before_success]
|
|
64
|
+
assert all(request.method == "GET" for request in requests)
|
|
65
|
+
assert all(
|
|
66
|
+
request.headers["authorization"] == requests[0].headers["authorization"]
|
|
67
|
+
for request in requests
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@pytest.mark.parametrize("timeout_type", [httpx.ConnectTimeout, httpx.ReadTimeout])
|
|
72
|
+
def test_fetch_openapi_raises_after_three_timeouts(mock_openapi_fetch, timeout_type):
|
|
73
|
+
outcomes, requests, delays = mock_openapi_fetch
|
|
74
|
+
last_error = timeout_type("Still unavailable")
|
|
75
|
+
outcomes.extend(
|
|
76
|
+
[
|
|
77
|
+
timeout_type("Temporary timeout"),
|
|
78
|
+
timeout_type("Temporary timeout"),
|
|
79
|
+
last_error,
|
|
80
|
+
]
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
with pytest.raises(timeout_type) as caught:
|
|
84
|
+
fetch_openapi("https://example.test/openapi", "test-user", "test-pass")
|
|
85
|
+
|
|
86
|
+
assert caught.value is last_error
|
|
87
|
+
assert len(requests) == 3
|
|
88
|
+
assert delays == [2, 4]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@pytest.mark.parametrize("status_code", [401, 503])
|
|
92
|
+
def test_fetch_openapi_does_not_retry_http_errors(mock_openapi_fetch, status_code):
|
|
93
|
+
outcomes, requests, delays = mock_openapi_fetch
|
|
94
|
+
outcomes.append(httpx.Response(status_code))
|
|
95
|
+
|
|
96
|
+
with pytest.raises(httpx.HTTPStatusError):
|
|
97
|
+
fetch_openapi("https://example.test/openapi", "test-user", "test-pass")
|
|
98
|
+
|
|
99
|
+
assert len(requests) == 1
|
|
100
|
+
assert delays == []
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_fetch_openapi_does_not_retry_invalid_json(mock_openapi_fetch):
|
|
104
|
+
outcomes, requests, delays = mock_openapi_fetch
|
|
105
|
+
outcomes.append(httpx.Response(200, text="not JSON"))
|
|
106
|
+
|
|
107
|
+
with pytest.raises(json.JSONDecodeError):
|
|
108
|
+
fetch_openapi("https://example.test/openapi", "test-user", "test-pass")
|
|
109
|
+
|
|
110
|
+
assert len(requests) == 1
|
|
111
|
+
assert delays == []
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@pytest.mark.parametrize("error_type", [httpx.ConnectError, httpx.WriteTimeout])
|
|
115
|
+
def test_fetch_openapi_does_not_retry_other_transport_errors(
|
|
116
|
+
mock_openapi_fetch, error_type
|
|
117
|
+
):
|
|
118
|
+
outcomes, requests, delays = mock_openapi_fetch
|
|
119
|
+
outcomes.append(error_type("Non-retryable transport failure"))
|
|
120
|
+
|
|
121
|
+
with pytest.raises(error_type):
|
|
122
|
+
fetch_openapi("https://example.test/openapi", "test-user", "test-pass")
|
|
123
|
+
|
|
124
|
+
assert len(requests) == 1
|
|
125
|
+
assert delays == []
|
|
126
|
+
|
|
127
|
+
|
|
17
128
|
def count_operations(spec: dict) -> int:
|
|
18
129
|
return sum(
|
|
19
130
|
1
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "3.0.81"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|