33import typing
44import uuid
55
6+ from marshmallow import Schema
7+ from marshmallow import fields as schema_fields
8+
69from commercetools import schemas , types
710from commercetools .testing .abstract import BaseModel , ServiceBackend
8- from commercetools .testing .utils import custom_fields_from_draft , get_product_variants
11+ from commercetools .testing .utils import (
12+ create_commercetools_response ,
13+ custom_fields_from_draft ,
14+ get_product_variants ,
15+ parse_request_params ,
16+ )
917
1018
1119class ProductsModel (BaseModel ):
@@ -149,17 +157,35 @@ def _create_price_from_draft(
149157 )
150158
151159
152- def _get_target_obj (obj : dict , action : types .ProductUpdateAction , default_staged = False ):
153- staged = getattr (action , "staged" , default_staged )
160+ def _get_target_obj (obj : dict , staged : bool ):
154161 if not staged and obj ["masterData" ]["current" ]:
155162 return obj ["masterData" ]["current" ]
156163 return obj ["masterData" ]["staged" ]
157164
158165
166+ def _get_variant (
167+ data_object : dict , * , id : str = "" , sku : str = ""
168+ ) -> typing .Optional [dict ]:
169+ if not data_object :
170+ return None
171+
172+ variants = [data_object ["masterVariant" ]]
173+ if data_object ["variants" ]:
174+ variants += data_object ["variants" ]
175+
176+ for variant in variants :
177+ if id and id == variant ["id" ]:
178+ return variant
179+ if sku and sku == variant ["sku" ]:
180+ return variant
181+
182+ return None
183+
184+
159185def _update_productdata_attr (dst : str , src : str ):
160186 def updater (self , obj : dict , action : types .ProductUpdateAction ):
161187 value = getattr (action , src )
162- target_obj = _get_target_obj (obj , action )
188+ target_obj = _get_target_obj (obj , getattr ( action , "staged" , False ) )
163189
164190 if target_obj [dst ] != value :
165191 new = copy .deepcopy (obj )
@@ -175,16 +201,9 @@ def updater(self, obj: dict, action: types.ProductSetAttributeAction):
175201 staged = getattr (action , "staged" , False )
176202 new = copy .deepcopy (obj )
177203
178- target_obj = _get_target_obj (new , action )
179-
180- variants = [target_obj ["masterVariant" ]]
181- if target_obj ["variants" ]:
182- variants += target_obj ["variants" ]
183-
184- for variant in variants :
185- if action .variant_id != variant ["id" ]:
186- continue
187-
204+ target_obj = _get_target_obj (new , staged )
205+ variant = _get_variant (target_obj , id = action .variant_id )
206+ if variant :
188207 for attr in variant ["attributes" ]:
189208 if attr ["name" ] == action .name :
190209 attr ["value" ] = action .value
@@ -195,7 +214,6 @@ def updater(self, obj: dict, action: types.ProductSetAttributeAction):
195214 variant ["attributes" ].append (
196215 attr_schema .dump (types .Attribute (name = action .name , value = action .value ))
197216 )
198- break
199217
200218 return new
201219
@@ -217,7 +235,7 @@ def updater(self, obj: dict, action: types.ProductAddVariantAction):
217235 schema = schemas .ProductVariantSchema ()
218236
219237 new = copy .deepcopy (obj )
220- target_obj = _get_target_obj (new , action )
238+ target_obj = _get_target_obj (new , staged = getattr ( action , "staged" , True ) )
221239 if not target_obj ["variants" ]:
222240 target_obj ["variants" ] = []
223241 target_obj ["variants" ].append (schema .dump (variant ))
@@ -243,7 +261,7 @@ def updater(self, obj: dict, action: types.ProductPublishAction):
243261def _set_product_prices ():
244262 def updater (self , obj : dict , action : types .ProductSetPricesAction ):
245263 new = copy .deepcopy (obj )
246- target_obj = _get_target_obj (new , action , default_staged = True )
264+ target_obj = _get_target_obj (new , getattr ( action , "staged" , True ) )
247265 prices = []
248266 for price_draft in action .prices :
249267 price = types .Price (
@@ -275,6 +293,12 @@ def updater(self, obj: dict, action: types.ProductSetPricesAction):
275293 return updater
276294
277295
296+ class UploadImageQuerySchema (Schema ):
297+ staged = schema_fields .Bool ()
298+ filename = schema_fields .Field ()
299+ sku = schema_fields .Field ()
300+
301+
278302class ProductsBackend (ServiceBackend ):
279303 service_path = "products"
280304 model_class = ProductsModel
@@ -292,6 +316,7 @@ def urls(self):
292316 ("^(?P<id>[^/]+)$" , "POST" , self .update_by_id ),
293317 ("^(?P<id>[^/]+)$" , "DELETE" , self .delete_by_id ),
294318 ("^key=(?P<key>[^/]+)$" , "DELETE" , self .delete_by_key ),
319+ ("^(?P<id>[^/]+)\/images$" , "POST" , self .upload_image ),
295320 ]
296321
297322 # Fixme: use decorator for this
@@ -302,3 +327,26 @@ def urls(self):
302327 "setPrices" : _set_product_prices (),
303328 "publish" : _publish_product_action (),
304329 }
330+
331+ def upload_image (self , request , id ):
332+ obj = self .model .get_by_id (id )
333+ if not obj :
334+ return create_commercetools_response (request , status_code = 404 )
335+
336+ obj = copy .deepcopy (obj )
337+
338+ params = parse_request_params (UploadImageQuerySchema , request )
339+ target = _get_target_obj (obj , staged = params ["staged" ])
340+ filename = params ["filename" ]
341+
342+ variant = _get_variant (target , sku = params ["sku" ])
343+ if not variant ["images" ]:
344+ variant ["images" ] = []
345+ image_schema = schemas .ImageSchema ()
346+ variant ["images" ].append (
347+ image_schema .dump (
348+ types .Image (url = f"cdn.commercetools.local/detail-{ filename } " )
349+ )
350+ )
351+ self .model .save (obj )
352+ return create_commercetools_response (request , json = obj )
0 commit comments