Make box_art_url optional and add legacy field handling in schemas

This commit is contained in:
Joakim Hellsén 2026-01-05 20:13:32 +01:00
commit 5504f59952
No known key found for this signature in database

View file

@ -31,7 +31,7 @@ class GameSchema(BaseModel):
twitch_id: str = Field(alias="id") # Present in both ViewerDropsDashboard and Inventory formats twitch_id: str = Field(alias="id") # Present in both ViewerDropsDashboard and Inventory formats
display_name: str | None = Field(default=None, alias="displayName") # Present in both formats display_name: str | None = Field(default=None, alias="displayName") # Present in both formats
box_art_url: str = Field(alias="boxArtURL") # Present in both formats box_art_url: str | None = Field(default=None, alias="boxArtURL") # Present in both formats, made optional
slug: str | None = None # Present in Inventory format slug: str | None = None # Present in Inventory format
name: str | None = None # Present in Inventory format (alternative to displayName) name: str | None = None # Present in Inventory format (alternative to displayName)
type_name: Literal["Game"] = Field(alias="__typename") # Present in both formats type_name: Literal["Game"] = Field(alias="__typename") # Present in both formats
@ -90,6 +90,9 @@ class DropBenefitSchema(BaseModel):
is_ios_available: bool = Field(default=False, alias="isIosAvailable") is_ios_available: bool = Field(default=False, alias="isIosAvailable")
distribution_type: str = Field(alias="distributionType") distribution_type: str = Field(alias="distributionType")
type_name: Literal["Benefit", "DropBenefit"] = Field(alias="__typename") type_name: Literal["Benefit", "DropBenefit"] = Field(alias="__typename")
# API response fields that should be ignored
game: dict | None = None
owner_organization: dict | None = Field(default=None, alias="ownerOrganization")
model_config = { model_config = {
"extra": "forbid", "extra": "forbid",
@ -168,6 +171,8 @@ class DropCampaign(BaseModel):
image_url: str | None = Field(default=None, alias="imageURL") image_url: str | None = Field(default=None, alias="imageURL")
allow: dict | None = None allow: dict | None = None
event_based_drops: list | None = Field(default=None, alias="eventBasedDrops") event_based_drops: list | None = Field(default=None, alias="eventBasedDrops")
# Legacy/API response fields that should be ignored
description: str | None = None
model_config = { model_config = {
"extra": "forbid", "extra": "forbid",
@ -197,11 +202,13 @@ class CurrentUser(BaseModel):
"""Schema for Twitch User objects. """Schema for Twitch User objects.
Handles both ViewerDropsDashboard and Inventory operation formats. Handles both ViewerDropsDashboard and Inventory operation formats.
Also handles legacy format with dropCampaign (singular) field.
""" """
twitch_id: str = Field(alias="id") twitch_id: str = Field(alias="id")
login: str | None = None login: str | None = None
drop_campaigns: list[DropCampaign] | None = Field(default=None, alias="dropCampaigns") drop_campaigns: list[DropCampaign] | None = Field(default=None, alias="dropCampaigns")
drop_campaign: DropCampaign | None = Field(default=None, alias="dropCampaign")
inventory: InventorySchema | None = None inventory: InventorySchema | None = None
type_name: Literal["User"] = Field(alias="__typename") type_name: Literal["User"] = Field(alias="__typename")
@ -214,24 +221,33 @@ class CurrentUser(BaseModel):
@model_validator(mode="after") @model_validator(mode="after")
def normalize_from_inventory(self) -> CurrentUser: def normalize_from_inventory(self) -> CurrentUser:
"""Normalize drop_campaigns from inventory if present. """Normalize drop_campaigns from inventory or singular dropCampaign.
If drop_campaigns is None but inventory exists, extract Handles three sources in order of priority:
dropCampaignsInProgress and assign it to drop_campaigns. 1. dropCampaigns (plural) - standard format
2. inventory.dropCampaignsInProgress - Inventory operation format
3. dropCampaign (singular) - legacy format
Returns: Returns:
The CurrentUser instance with normalized drop_campaigns. The CurrentUser instance with normalized drop_campaigns.
""" """
if self.drop_campaigns is None and self.inventory is not None: if self.drop_campaigns is None:
if self.inventory is not None:
self.drop_campaigns = self.inventory.drop_campaigns_in_progress self.drop_campaigns = self.inventory.drop_campaigns_in_progress
elif self.drop_campaign is not None:
self.drop_campaigns = [self.drop_campaign]
return self return self
class Data(BaseModel): class Data(BaseModel):
"""Schema for the data field in Twitch API responses.""" """Schema for the data field in Twitch API responses.
current_user: CurrentUser | None = Field(alias="currentUser") Handles both currentUser (standard) and user (legacy) field names.
"""
current_user: CurrentUser | None = Field(default=None, alias="currentUser")
user: CurrentUser | None = Field(default=None, alias="user")
model_config = { model_config = {
"extra": "forbid", "extra": "forbid",
@ -255,6 +271,20 @@ class Data(BaseModel):
return None return None
return v return v
@model_validator(mode="after")
def normalize_user_field(self) -> Data:
"""Normalize user field to current_user if needed.
If current_user is None but user exists, use user as current_user.
Returns:
The Data instance with normalized current_user.
"""
if self.current_user is None and self.user is not None:
self.current_user = self.user
return self
class Extensions(BaseModel): class Extensions(BaseModel):
"""Schema for the extensions field in GraphQL responses.""" """Schema for the extensions field in GraphQL responses."""