Skip to content

Commit a3c8786

Browse files
refactor(inventory): dedupe shared product-knowledge fields
Extract the ~30 fields identical between InventoryProductKnowledge and InventorySetProductRequest into an InventoryMerchandisingFields base class. Fixes the SonarCloud duplication gate (32.4% on new code) without changing the serialized shape of either class.
1 parent 2757bce commit a3c8786

2 files changed

Lines changed: 41 additions & 126 deletions

File tree

‎checkout_sdk/inventory/inventory.py‎

Lines changed: 37 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -186,27 +186,13 @@ class InventoryProductKnowledgeLinks:
186186
delete: InventoryHalLink
187187

188188

189-
class InventoryProductKnowledge:
190-
"""Response body for getInventoryProduct and setInventoryProduct. Also embeddable as
191-
`InventoryLevels.product` when `?expand=product` is passed.
192-
193-
Beta: this schema and the endpoints that return it are marked Beta in the specification.
189+
class InventoryMerchandisingFields:
190+
"""Merchandising fields shared, with identical shape and constraints, between
191+
`InventoryProductKnowledge` (response) and `InventorySetProductRequest` (request). Split out
192+
to avoid duplicating ~30 identical field declarations; fields that differ in constraints or
193+
required-ness between the two (`title`, `description`, `product_url`, `image_url`, `sku`,
194+
`condition`) stay on the subclasses.
194195
"""
195-
# Identifier of the variant this product knowledge describes.
196-
# [Required]
197-
variant_id: str
198-
# Product title.
199-
# [Required]
200-
title: str
201-
# Product description.
202-
# [Required]
203-
description: str
204-
# Canonical URL of the product page.
205-
# [Required]
206-
product_url: str
207-
# URL of the primary product image.
208-
# [Required]
209-
image_url: str
210196
# Additional image URLs.
211197
# [Optional]
212198
additional_image_urls: list # values of str
@@ -216,9 +202,6 @@ class InventoryProductKnowledge:
216202
# URL of a 3D model of the product.
217203
# [Optional]
218204
model_3d_url: str
219-
# Merchant SKU.
220-
# [Optional]
221-
sku: str
222205
# Global Trade Item Number.
223206
# [Optional]
224207
gtin: str
@@ -262,10 +245,6 @@ class InventoryProductKnowledge:
262245
# Target gender.
263246
# [Optional]
264247
gender: str
265-
# Condition of the item. Defaults to `new`.
266-
# [Required]
267-
# enum: new, used, refurbished
268-
condition: InventoryCondition
269248
# Material composition.
270249
# [Optional]
271250
material: str
@@ -311,6 +290,36 @@ class InventoryProductKnowledge:
311290
# URL of the seller's terms of service.
312291
# [Optional]
313292
seller_tos: str
293+
294+
295+
class InventoryProductKnowledge(InventoryMerchandisingFields):
296+
"""Response body for getInventoryProduct and setInventoryProduct. Also embeddable as
297+
`InventoryLevels.product` when `?expand=product` is passed.
298+
299+
Beta: this schema and the endpoints that return it are marked Beta in the specification.
300+
"""
301+
# Identifier of the variant this product knowledge describes.
302+
# [Required]
303+
variant_id: str
304+
# Product title.
305+
# [Required]
306+
title: str
307+
# Product description.
308+
# [Required]
309+
description: str
310+
# Canonical URL of the product page.
311+
# [Required]
312+
product_url: str
313+
# URL of the primary product image.
314+
# [Required]
315+
image_url: str
316+
# Merchant SKU.
317+
# [Optional]
318+
sku: str
319+
# Condition of the item. Defaults to `new`.
320+
# [Required]
321+
# enum: new, used, refurbished
322+
condition: InventoryCondition
314323
# When the product knowledge was created.
315324
# [Required]
316325
created_on: datetime
@@ -322,7 +331,7 @@ class InventoryProductKnowledge:
322331
_links: InventoryProductKnowledgeLinks
323332

324333

325-
class InventorySetProductRequest:
334+
class InventorySetProductRequest(InventoryMerchandisingFields):
326335
"""Request body for PUT /inventory/{variant_id}/product.
327336
328337
Beta: this schema and the endpoint it targets are marked Beta in the specification.
@@ -343,111 +352,14 @@ class InventorySetProductRequest:
343352
# [Required]
344353
# max 2048 characters
345354
image_url: str
346-
# Additional image URLs.
347-
# [Optional]
348-
additional_image_urls: list # values of str
349-
# URL of a product video.
350-
# [Optional]
351-
video_url: str
352-
# URL of a 3D model of the product.
353-
# [Optional]
354-
model_3d_url: str
355355
# Merchant SKU.
356356
# [Optional]
357357
# max 128 characters
358358
sku: str
359-
# Global Trade Item Number.
360-
# [Optional]
361-
gtin: str
362-
# Manufacturer Part Number.
363-
# [Optional]
364-
mpn: str
365-
# Brand name.
366-
# [Optional]
367-
brand: str
368-
# Product category.
369-
# [Optional]
370-
category: str
371-
# Regular price.
372-
# [Optional]
373-
price: InventoryMoney
374-
# Discounted price. Must share `price`'s currency and be <= `price`.
375-
# [Optional]
376-
sale_price: InventoryMoney
377-
# Start of the sale price window. Pairs with `sale_price`.
378-
# [Optional]
379-
sale_price_starts_at: datetime
380-
# End of the sale price window. Pairs with `sale_price`.
381-
# [Optional]
382-
sale_price_ends_at: datetime
383-
# Identifier grouping variants of the same product. When set, `color` and `size` are both
384-
# expected.
385-
# [Optional]
386-
group_id: str
387-
# Title shared across all variants in `group_id`.
388-
# [Optional]
389-
group_title: str
390-
# Color of this variant. Expected when `group_id` is set.
391-
# [Optional]
392-
color: str
393-
# Size of this variant. Expected when `group_id` is set.
394-
# [Optional]
395-
size: str
396-
# Sizing system used by `size`.
397-
# [Optional]
398-
size_system: str
399-
# Target gender.
400-
# [Optional]
401-
gender: str
402359
# Condition of the item. Exact lowercase match. Defaults to `new`.
403360
# [Optional]
404361
# enum: new, used, refurbished
405362
condition: InventoryCondition
406-
# Material composition.
407-
# [Optional]
408-
material: str
409-
# Target age group.
410-
# [Optional]
411-
age_group: str
412-
# Length of the item.
413-
# [Optional]
414-
length: float
415-
# Width of the item.
416-
# [Optional]
417-
width: float
418-
# Height of the item.
419-
# [Optional]
420-
height: float
421-
# Unit used by `length`, `width` and `height`.
422-
# [Optional]
423-
dimension_unit: str
424-
# Weight of the item.
425-
# [Optional]
426-
weight: float
427-
# Unit used by `weight`.
428-
# [Optional]
429-
weight_unit: str
430-
# Expiration date of the item, if applicable.
431-
# [Optional]
432-
expiration_date: datetime
433-
# Harmonized System code, for customs.
434-
# [Optional]
435-
harmonized_system_code: str
436-
# 2-letter ISO 3166-1 alpha-2 country of origin.
437-
# [Optional]
438-
country_of_origin: str
439-
# Name of the seller.
440-
# [Optional]
441-
seller_name: str
442-
# URL of the seller.
443-
# [Optional]
444-
seller_url: str
445-
# URL of the seller's privacy policy.
446-
# [Optional]
447-
seller_privacy_policy: str
448-
# URL of the seller's terms of service.
449-
# [Optional]
450-
seller_tos: str
451363

452364

453365
class InventoryLevels:

‎tests/inventory/inventory_serialization_test.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import typing
23

34
from checkout_sdk.inventory.inventory import (
45
InventoryAdjustmentRequest, InventoryCondition, InventoryHalLink, InventoryLevels,
@@ -271,7 +272,9 @@ def test_serializes_every_property(self):
271272
assert serialized['sale_price'] == {'amount': 1499, 'currency': 'USD'}
272273
assert serialized['condition'] == 'new'
273274
assert serialized['country_of_origin'] == 'US'
274-
assert set(serialized.keys()) == set(InventorySetProductRequest.__annotations__.keys())
275+
# get_type_hints merges the InventoryMerchandisingFields base in with the subclass's
276+
# own annotations; __annotations__ alone would only see the subclass's direct fields.
277+
assert set(serialized.keys()) == set(typing.get_type_hints(InventorySetProductRequest).keys())
275278

276279
def test_condition_enum_defaults_to_new_and_carries_three_values(self):
277280
assert [e.value for e in InventoryCondition] == ['new', 'used', 'refurbished']

0 commit comments

Comments
 (0)