Add structured workflow result models for video and document uploads; update client methods and examples
This commit is contained in:
parent
6d54c5900c
commit
00539b4fb2
5 changed files with 98 additions and 26 deletions
|
|
@ -13,6 +13,7 @@ Currently wired API domains include Notification, Inventory, Fulfillment, Accoun
|
||||||
|
|
||||||
The Media wrapper includes workflow helpers on top of the raw endpoints:
|
The Media wrapper includes workflow helpers on top of the raw endpoints:
|
||||||
|
|
||||||
|
- `VideoWorkflowResult` and `DocumentWorkflowResult` to return structured data from the higher-level workflows
|
||||||
- `extract_resource_id()` to pull a media resource ID from a `Location` header
|
- `extract_resource_id()` to pull a media resource ID from a `Location` header
|
||||||
- `guess_media_content_type()` to infer a content type from a file name when possible
|
- `guess_media_content_type()` to infer a content type from a file name when possible
|
||||||
- `wait_for_video()` to poll until a video reaches `LIVE` or a terminal failure state
|
- `wait_for_video()` to poll until a video reaches `LIVE` or a terminal failure state
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,17 @@
|
||||||
from ebay_client.media.client import CreatedMediaResource, MediaClient, extract_resource_id
|
from ebay_client.media.client import (
|
||||||
|
CreatedMediaResource,
|
||||||
|
DocumentWorkflowResult,
|
||||||
|
MediaClient,
|
||||||
|
VideoWorkflowResult,
|
||||||
|
extract_resource_id,
|
||||||
|
guess_media_content_type,
|
||||||
|
)
|
||||||
|
|
||||||
__all__ = ["CreatedMediaResource", "MediaClient", "extract_resource_id"]
|
__all__ = [
|
||||||
|
"CreatedMediaResource",
|
||||||
|
"DocumentWorkflowResult",
|
||||||
|
"MediaClient",
|
||||||
|
"VideoWorkflowResult",
|
||||||
|
"extract_resource_id",
|
||||||
|
"guess_media_content_type",
|
||||||
|
]
|
||||||
|
|
@ -27,6 +27,19 @@ class CreatedMediaResource(BaseModel):
|
||||||
resource_id: str | None = None
|
resource_id: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class VideoWorkflowResult(BaseModel):
|
||||||
|
created: CreatedMediaResource
|
||||||
|
video: Video
|
||||||
|
video_id: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class DocumentWorkflowResult(BaseModel):
|
||||||
|
created: CreateDocumentResponse
|
||||||
|
uploaded: DocumentResponse | None = None
|
||||||
|
document: DocumentResponse
|
||||||
|
document_id: str | None = None
|
||||||
|
|
||||||
|
|
||||||
def extract_resource_id(location: str | None) -> str | None:
|
def extract_resource_id(location: str | None) -> str | None:
|
||||||
if not location:
|
if not location:
|
||||||
return None
|
return None
|
||||||
|
|
@ -109,7 +122,7 @@ class MediaClient:
|
||||||
content_range: str | None = None,
|
content_range: str | None = None,
|
||||||
timeout_seconds: float = 30.0,
|
timeout_seconds: float = 30.0,
|
||||||
poll_interval_seconds: float = 1.0,
|
poll_interval_seconds: float = 1.0,
|
||||||
) -> Video:
|
) -> VideoWorkflowResult:
|
||||||
created = self.create_video(payload)
|
created = self.create_video(payload)
|
||||||
video_id = self._require_resource_id(created.resource_id, "video resource ID")
|
video_id = self._require_resource_id(created.resource_id, "video resource ID")
|
||||||
self.upload_video(
|
self.upload_video(
|
||||||
|
|
@ -118,11 +131,12 @@ class MediaClient:
|
||||||
content_length=content_length if content_length is not None else len(content),
|
content_length=content_length if content_length is not None else len(content),
|
||||||
content_range=content_range,
|
content_range=content_range,
|
||||||
)
|
)
|
||||||
return self.wait_for_video(
|
video = self.wait_for_video(
|
||||||
video_id,
|
video_id,
|
||||||
timeout_seconds=timeout_seconds,
|
timeout_seconds=timeout_seconds,
|
||||||
poll_interval_seconds=poll_interval_seconds,
|
poll_interval_seconds=poll_interval_seconds,
|
||||||
)
|
)
|
||||||
|
return VideoWorkflowResult(created=created, video=video, video_id=video_id)
|
||||||
|
|
||||||
def create_upload_and_wait_video_from_path(
|
def create_upload_and_wait_video_from_path(
|
||||||
self,
|
self,
|
||||||
|
|
@ -133,7 +147,7 @@ class MediaClient:
|
||||||
description: str | None = None,
|
description: str | None = None,
|
||||||
timeout_seconds: float = 30.0,
|
timeout_seconds: float = 30.0,
|
||||||
poll_interval_seconds: float = 1.0,
|
poll_interval_seconds: float = 1.0,
|
||||||
) -> Video:
|
) -> VideoWorkflowResult:
|
||||||
path = Path(video_path)
|
path = Path(video_path)
|
||||||
content = path.read_bytes()
|
content = path.read_bytes()
|
||||||
payload = CreateVideoRequest(
|
payload = CreateVideoRequest(
|
||||||
|
|
@ -198,20 +212,26 @@ class MediaClient:
|
||||||
content_type: str = "application/octet-stream",
|
content_type: str = "application/octet-stream",
|
||||||
timeout_seconds: float = 30.0,
|
timeout_seconds: float = 30.0,
|
||||||
poll_interval_seconds: float = 1.0,
|
poll_interval_seconds: float = 1.0,
|
||||||
) -> DocumentResponse:
|
) -> DocumentWorkflowResult:
|
||||||
created = self.create_document(payload)
|
created = self.create_document(payload)
|
||||||
document_id = self._require_resource_id(created.documentId, "documentId")
|
document_id = self._require_resource_id(created.documentId, "documentId")
|
||||||
self.upload_document(
|
uploaded = self.upload_document(
|
||||||
document_id,
|
document_id,
|
||||||
file_name=file_name,
|
file_name=file_name,
|
||||||
content=content,
|
content=content,
|
||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
)
|
)
|
||||||
return self.wait_for_document(
|
document = self.wait_for_document(
|
||||||
document_id,
|
document_id,
|
||||||
timeout_seconds=timeout_seconds,
|
timeout_seconds=timeout_seconds,
|
||||||
poll_interval_seconds=poll_interval_seconds,
|
poll_interval_seconds=poll_interval_seconds,
|
||||||
)
|
)
|
||||||
|
return DocumentWorkflowResult(
|
||||||
|
created=created,
|
||||||
|
uploaded=uploaded,
|
||||||
|
document=document,
|
||||||
|
document_id=document_id,
|
||||||
|
)
|
||||||
|
|
||||||
def create_document_from_url(self, payload: CreateDocumentFromUrlRequest) -> CreateDocumentResponse:
|
def create_document_from_url(self, payload: CreateDocumentFromUrlRequest) -> CreateDocumentResponse:
|
||||||
return self.transport.request_model(
|
return self.transport.request_model(
|
||||||
|
|
@ -229,14 +249,20 @@ class MediaClient:
|
||||||
*,
|
*,
|
||||||
timeout_seconds: float = 30.0,
|
timeout_seconds: float = 30.0,
|
||||||
poll_interval_seconds: float = 1.0,
|
poll_interval_seconds: float = 1.0,
|
||||||
) -> DocumentResponse:
|
) -> DocumentWorkflowResult:
|
||||||
created = self.create_document_from_url(payload)
|
created = self.create_document_from_url(payload)
|
||||||
document_id = self._require_resource_id(created.documentId, "documentId")
|
document_id = self._require_resource_id(created.documentId, "documentId")
|
||||||
return self.wait_for_document(
|
document = self.wait_for_document(
|
||||||
document_id,
|
document_id,
|
||||||
timeout_seconds=timeout_seconds,
|
timeout_seconds=timeout_seconds,
|
||||||
poll_interval_seconds=poll_interval_seconds,
|
poll_interval_seconds=poll_interval_seconds,
|
||||||
)
|
)
|
||||||
|
return DocumentWorkflowResult(
|
||||||
|
created=created,
|
||||||
|
uploaded=None,
|
||||||
|
document=document,
|
||||||
|
document_id=document_id,
|
||||||
|
)
|
||||||
|
|
||||||
def get_document(self, document_id: str) -> DocumentResponse:
|
def get_document(self, document_id: str) -> DocumentResponse:
|
||||||
return self.transport.request_model(
|
return self.transport.request_model(
|
||||||
|
|
@ -278,7 +304,7 @@ class MediaClient:
|
||||||
*,
|
*,
|
||||||
timeout_seconds: float = 30.0,
|
timeout_seconds: float = 30.0,
|
||||||
poll_interval_seconds: float = 1.0,
|
poll_interval_seconds: float = 1.0,
|
||||||
) -> DocumentResponse:
|
) -> DocumentWorkflowResult:
|
||||||
path = Path(document_path)
|
path = Path(document_path)
|
||||||
return self.create_upload_and_wait_document(
|
return self.create_upload_and_wait_document(
|
||||||
payload,
|
payload,
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ def upload_image_from_url(client: EbayClient, image_url: str) -> None:
|
||||||
|
|
||||||
|
|
||||||
def upload_document_and_wait(client: EbayClient, document_path: Path) -> None:
|
def upload_document_and_wait(client: EbayClient, document_path: Path) -> None:
|
||||||
accepted = client.media.create_upload_and_wait_document(
|
result = client.media.create_upload_and_wait_document(
|
||||||
CreateDocumentRequest(
|
CreateDocumentRequest(
|
||||||
documentType="USER_GUIDE_OR_MANUAL",
|
documentType="USER_GUIDE_OR_MANUAL",
|
||||||
languages=["en-US"],
|
languages=["en-US"],
|
||||||
|
|
@ -42,11 +42,12 @@ def upload_document_and_wait(client: EbayClient, document_path: Path) -> None:
|
||||||
content_type="application/pdf",
|
content_type="application/pdf",
|
||||||
timeout_seconds=60.0,
|
timeout_seconds=60.0,
|
||||||
)
|
)
|
||||||
print("document_final_status:", accepted.documentStatus)
|
print("document_id:", result.document_id)
|
||||||
|
print("document_final_status:", result.document.documentStatus)
|
||||||
|
|
||||||
|
|
||||||
def upload_document_and_wait_from_path(client: EbayClient, document_path: Path) -> None:
|
def upload_document_and_wait_from_path(client: EbayClient, document_path: Path) -> None:
|
||||||
accepted = client.media.create_upload_and_wait_document_from_path(
|
result = client.media.create_upload_and_wait_document_from_path(
|
||||||
CreateDocumentRequest(
|
CreateDocumentRequest(
|
||||||
documentType="USER_GUIDE_OR_MANUAL",
|
documentType="USER_GUIDE_OR_MANUAL",
|
||||||
languages=["en-US"],
|
languages=["en-US"],
|
||||||
|
|
@ -54,17 +55,18 @@ def upload_document_and_wait_from_path(client: EbayClient, document_path: Path)
|
||||||
document_path,
|
document_path,
|
||||||
timeout_seconds=60.0,
|
timeout_seconds=60.0,
|
||||||
)
|
)
|
||||||
print("document_final_status:", accepted.documentStatus)
|
print("document_id:", result.document_id)
|
||||||
|
print("document_final_status:", result.document.documentStatus)
|
||||||
|
|
||||||
|
|
||||||
def upload_video_and_wait(client: EbayClient, video_path: Path) -> None:
|
def upload_video_and_wait(client: EbayClient, video_path: Path) -> None:
|
||||||
live_video = client.media.create_upload_and_wait_video_from_path(
|
result = client.media.create_upload_and_wait_video_from_path(
|
||||||
video_path,
|
video_path,
|
||||||
description="Example upload from the ebay-rest-client workspace.",
|
description="Example upload from the ebay-rest-client workspace.",
|
||||||
timeout_seconds=120.0,
|
timeout_seconds=120.0,
|
||||||
)
|
)
|
||||||
print("video_status:", live_video.status)
|
print("video_id:", result.video_id)
|
||||||
print("video_id:", live_video.videoId)
|
print("video_status:", result.video.status)
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,14 @@ from ebay_client.generated.notification.models import (
|
||||||
UpdateSubscriptionRequest,
|
UpdateSubscriptionRequest,
|
||||||
)
|
)
|
||||||
from ebay_client.inventory.client import InventoryClient
|
from ebay_client.inventory.client import InventoryClient
|
||||||
from ebay_client.media.client import CreatedMediaResource, MediaClient, extract_resource_id, guess_media_content_type
|
from ebay_client.media.client import (
|
||||||
|
CreatedMediaResource,
|
||||||
|
DocumentWorkflowResult,
|
||||||
|
MediaClient,
|
||||||
|
VideoWorkflowResult,
|
||||||
|
extract_resource_id,
|
||||||
|
guess_media_content_type,
|
||||||
|
)
|
||||||
from ebay_client.notification.client import NotificationClient
|
from ebay_client.notification.client import NotificationClient
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -593,7 +600,10 @@ def test_media_create_upload_and_wait_video_orchestrates_flow(monkeypatch) -> No
|
||||||
poll_interval_seconds=0.0,
|
poll_interval_seconds=0.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result.videoId == "VIDEO-9"
|
assert isinstance(result, VideoWorkflowResult)
|
||||||
|
assert result.video_id == "VIDEO-9"
|
||||||
|
assert result.video.videoId == "VIDEO-9"
|
||||||
|
assert result.created.resource_id == "VIDEO-9"
|
||||||
assert calls[0][0] == "create_video"
|
assert calls[0][0] == "create_video"
|
||||||
assert calls[1] == (
|
assert calls[1] == (
|
||||||
"upload_video",
|
"upload_video",
|
||||||
|
|
@ -636,7 +646,11 @@ def test_media_create_upload_and_wait_document_orchestrates_flow(monkeypatch) ->
|
||||||
poll_interval_seconds=0.0,
|
poll_interval_seconds=0.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result.documentStatus == "ACCEPTED"
|
assert isinstance(result, DocumentWorkflowResult)
|
||||||
|
assert result.document_id == "DOC-9"
|
||||||
|
assert result.created.documentId == "DOC-9"
|
||||||
|
assert result.uploaded is not None and result.uploaded.documentStatus == "SUBMITTED"
|
||||||
|
assert result.document.documentStatus == "ACCEPTED"
|
||||||
assert calls[0][0] == "create_document"
|
assert calls[0][0] == "create_document"
|
||||||
assert calls[1] == (
|
assert calls[1] == (
|
||||||
"upload_document",
|
"upload_document",
|
||||||
|
|
@ -679,7 +693,11 @@ def test_media_create_document_from_url_and_wait_orchestrates_flow(monkeypatch)
|
||||||
poll_interval_seconds=0.0,
|
poll_interval_seconds=0.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result.documentStatus == "ACCEPTED"
|
assert isinstance(result, DocumentWorkflowResult)
|
||||||
|
assert result.document_id == "DOC-10"
|
||||||
|
assert result.created.documentId == "DOC-10"
|
||||||
|
assert result.uploaded is None
|
||||||
|
assert result.document.documentStatus == "ACCEPTED"
|
||||||
assert calls[0][0] == "create_document_from_url"
|
assert calls[0][0] == "create_document_from_url"
|
||||||
assert calls[1] == (
|
assert calls[1] == (
|
||||||
"wait_for_document",
|
"wait_for_document",
|
||||||
|
|
@ -788,7 +806,12 @@ def test_media_create_upload_and_wait_document_from_path_reads_file_and_delegate
|
||||||
client,
|
client,
|
||||||
"create_upload_and_wait_document",
|
"create_upload_and_wait_document",
|
||||||
lambda payload, **kwargs: captured.update({"payload": payload, **kwargs})
|
lambda payload, **kwargs: captured.update({"payload": payload, **kwargs})
|
||||||
or DocumentResponse(documentId="DOC-77", documentStatus="ACCEPTED"),
|
or DocumentWorkflowResult(
|
||||||
|
created=CreateDocumentResponse(documentId="DOC-77", documentStatus="PENDING_UPLOAD"),
|
||||||
|
uploaded=DocumentResponse(documentId="DOC-77", documentStatus="SUBMITTED"),
|
||||||
|
document=DocumentResponse(documentId="DOC-77", documentStatus="ACCEPTED"),
|
||||||
|
document_id="DOC-77",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
result = client.create_upload_and_wait_document_from_path(
|
result = client.create_upload_and_wait_document_from_path(
|
||||||
|
|
@ -797,7 +820,8 @@ def test_media_create_upload_and_wait_document_from_path_reads_file_and_delegate
|
||||||
poll_interval_seconds=0.0,
|
poll_interval_seconds=0.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result.documentStatus == "ACCEPTED"
|
assert isinstance(result, DocumentWorkflowResult)
|
||||||
|
assert result.document.documentStatus == "ACCEPTED"
|
||||||
assert captured["file_name"] == "guide.pdf"
|
assert captured["file_name"] == "guide.pdf"
|
||||||
assert captured["content"] == b"%PDF-1.7"
|
assert captured["content"] == b"%PDF-1.7"
|
||||||
assert captured["content_type"] == "application/pdf"
|
assert captured["content_type"] == "application/pdf"
|
||||||
|
|
@ -815,7 +839,11 @@ def test_media_create_upload_and_wait_video_from_path_builds_payload_and_delegat
|
||||||
client,
|
client,
|
||||||
"create_upload_and_wait_video",
|
"create_upload_and_wait_video",
|
||||||
lambda payload, **kwargs: captured.update({"payload": payload, **kwargs})
|
lambda payload, **kwargs: captured.update({"payload": payload, **kwargs})
|
||||||
or Video(videoId="VIDEO-88", status="LIVE"),
|
or VideoWorkflowResult(
|
||||||
|
created=CreatedMediaResource(resource_id="VIDEO-88"),
|
||||||
|
video=Video(videoId="VIDEO-88", status="LIVE"),
|
||||||
|
video_id="VIDEO-88",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
result = client.create_upload_and_wait_video_from_path(
|
result = client.create_upload_and_wait_video_from_path(
|
||||||
|
|
@ -824,7 +852,8 @@ def test_media_create_upload_and_wait_video_from_path_builds_payload_and_delegat
|
||||||
poll_interval_seconds=0.0,
|
poll_interval_seconds=0.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result.videoId == "VIDEO-88"
|
assert isinstance(result, VideoWorkflowResult)
|
||||||
|
assert result.video.videoId == "VIDEO-88"
|
||||||
payload = captured["payload"]
|
payload = captured["payload"]
|
||||||
assert isinstance(payload, CreateVideoRequest)
|
assert isinstance(payload, CreateVideoRequest)
|
||||||
assert payload.title == "demo"
|
assert payload.title == "demo"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue