Skip to content

Commit

Permalink
Add path field to ProductVariantBulkError (saleor#12534)
Browse files Browse the repository at this point in the history
  • Loading branch information
szdrasiak authored Apr 17, 2023
1 parent 7465cc8 commit 05df3be
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 106 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ All notable, unreleased changes to this project will be documented in this file.
3.14.0 [Unreleased]

### Breaking changes
- `path` field for errors related with product variants input in `ProductBulkCreate` will return more detailed paths: `variants.1.stocks.0.warehouse` instead of `variants.1.warehouses` - #12534 by @SzymJ

### GraphQL API
- Add `path` field to `ProductVariantBulkError` - #12534 by @SzymJ

### Saleor Apps

Expand Down
14 changes: 13 additions & 1 deletion saleor/graphql/core/types/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@
DOC_CATEGORY_USERS,
DOC_CATEGORY_WEBHOOKS,
)
from ..descriptions import ADDED_IN_36, ADDED_IN_312, DEPRECATED_IN_3X_FIELD
from ..descriptions import (
ADDED_IN_36,
ADDED_IN_312,
ADDED_IN_314,
DEPRECATED_IN_3X_FIELD,
)
from ..enums import (
AccountErrorCode,
AppErrorCode,
Expand Down Expand Up @@ -456,6 +461,13 @@ class Meta:

class ProductVariantBulkError(Error):
code = ProductVariantBulkErrorCode(description="The error code.", required=True)
path = graphene.String(
description=(
"Path to field that caused the error. A value of `null` indicates that "
"the error isn't associated with a particular field." + ADDED_IN_314
),
required=False,
)
attributes = NonNullList(
graphene.ID,
description="List of attributes IDs which causes the error.",
Expand Down
4 changes: 2 additions & 2 deletions saleor/graphql/product/bulk_mutations/product_bulk_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,8 +488,8 @@ def clean_variants(
for error in errors:
index_error_map[product_index].append(
ProductBulkCreateError(
path=f"variants.{index}.{error.field}"
if error.field
path=f"variants.{index}.{error.path}"
if error.path
else f"variants.{index}",
message=error.message,
code=error.code,
Expand Down
137 changes: 79 additions & 58 deletions saleor/graphql/product/bulk_mutations/product_variant_bulk_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,18 @@ def clean_price(
currency,
channel_id,
variant_index,
listing_index,
errors,
index_error_map,
path_prefix,
):
try:
validate_price_precision(price, currency, CURRENCY_FRACTIONS)
except ValidationError as error:
index_error_map[variant_index].append(
ProductVariantBulkError(
field=to_camel_case(field_name),
path=f"{path_prefix}.{listing_index}.{to_camel_case(field_name)}",
message=error.message,
code=ProductVariantBulkErrorCode.INVALID_PRICE.value,
channels=[channel_id],
Expand Down Expand Up @@ -287,6 +290,7 @@ def clean_attributes(
index_error_map[variant_index].append(
ProductVariantBulkError(
field="attributes",
path="attributes",
message=message,
code=code,
attributes=invalid_attributes,
Expand Down Expand Up @@ -322,6 +326,7 @@ def clean_attributes(
index_error_map[variant_index].append(
ProductVariantBulkError(
field="attributes",
path="attributes",
message=error.message,
code=error.code,
attributes=attributes,
Expand All @@ -336,6 +341,7 @@ def clean_attributes(
index_error_map[variant_index].append(
ProductVariantBulkError(
field="attributes",
path="attributes",
message=message,
code=ProductVariantBulkErrorCode.INVALID.value,
attributes=invalid_attributes,
Expand Down Expand Up @@ -363,26 +369,32 @@ def clean_prices(
currency_code,
channel_id,
variant_index,
listing_index,
errors,
index_error_map,
path_prefix,
):
clean_price(
price,
"price",
currency_code,
channel_id,
variant_index,
listing_index,
errors,
index_error_map,
path_prefix,
)
clean_price(
cost_price,
"cost_price",
currency_code,
channel_id,
variant_index,
listing_index,
errors,
index_error_map,
path_prefix,
)

@classmethod
Expand All @@ -393,6 +405,7 @@ def clean_channel_listings(
errors,
variant_index,
index_error_map,
path_prefix="channelListings",
):
channel_ids = [
channel_listing["channel_id"] for channel_listing in channel_listings
Expand All @@ -401,18 +414,9 @@ def clean_channel_listings(

duplicates = get_duplicated_values(channel_ids)
if duplicates:
message = "Duplicated channel ID."
index_error_map[variant_index].append(
ProductVariantBulkError(
field="channelListings",
message=message,
code=ProductVariantBulkErrorCode.DUPLICATED_INPUT_ITEM.value,
channels=duplicates,
)
)
if errors is not None:
errors["channel_listings"] = ValidationError(
message=message,
message="Duplicated channel ID.",
code=ProductVariantBulkErrorCode.DUPLICATED_INPUT_ITEM.value,
params={"channels": duplicates, "index": variant_index},
)
Expand All @@ -424,20 +428,11 @@ def clean_channel_listings(
]

if channels_not_assigned_to_product:
message = "Product not available in channels."
code = ProductVariantBulkErrorCode.PRODUCT_NOT_ASSIGNED_TO_CHANNEL.value
index_error_map[variant_index].append(
ProductVariantBulkError(
field="channelId",
message=message,
code=code,
channels=channels_not_assigned_to_product,
)
)
if errors is not None:
errors["channel_id"].append(
ValidationError(
message=message,
message="Product not available in channels.",
code=code,
params={
"index": variant_index,
Expand All @@ -446,13 +441,33 @@ def clean_channel_listings(
)
)

for channel_listing in channel_listings:
for listing_index, channel_listing in enumerate(channel_listings):
channel_id = channel_listing["channel_id"]
errors_count_before_prices = len(index_error_map[variant_index])

if (
channel_id in channels_not_assigned_to_product
or channel_id in duplicates
):
if channel_id in channels_not_assigned_to_product:
code = ProductVariantBulkErrorCode.PRODUCT_NOT_ASSIGNED_TO_CHANNEL.value
index_error_map[variant_index].append(
ProductVariantBulkError(
field="channelId",
path=f"{path_prefix}.{listing_index}.channelId",
message="Product not available in channels.",
code=code,
channels=[channel_id],
)
)
continue

if channel_id in duplicates:
index_error_map[variant_index].append(
ProductVariantBulkError(
field="channelId",
path=f"{path_prefix}.{listing_index}.channelId",
message="Duplicated channel ID.",
code=ProductVariantBulkErrorCode.DUPLICATED_INPUT_ITEM.value,
channels=[channel_id],
)
)
continue

channel_listing["channel"] = product_channel_global_id_to_instance_map[
Expand All @@ -462,16 +477,16 @@ def clean_channel_listings(
cost_price = channel_listing.get("cost_price")
currency_code = channel_listing["channel"].currency_code

errors_count_before_prices = len(index_error_map[variant_index])

cls.clean_prices(
price,
cost_price,
currency_code,
channel_id,
variant_index,
listing_index,
errors,
index_error_map,
path_prefix,
)

if len(index_error_map[variant_index]) > errors_count_before_prices:
Expand All @@ -489,6 +504,7 @@ def clean_stocks(
errors,
variant_index,
index_error_map,
path_prefix="stocks",
):
stocks_to_create = []
warehouse_ids = [stock["warehouse"] for stock in stocks_data]
Expand All @@ -500,51 +516,51 @@ def clean_stocks(
}

if wrong_warehouse_ids:
message = "Not existing warehouse ID."
index_error_map[variant_index].append(
ProductVariantBulkError(
field="warehouses",
message=message,
code=ProductVariantBulkErrorCode.NOT_FOUND.value,
warehouses=wrong_warehouse_ids,
)
)
if errors is not None:
errors["warehouses"] = ValidationError(
message,
"Not existing warehouse ID.",
code=ProductVariantBulkErrorCode.NOT_FOUND.value,
params={"warehouses": wrong_warehouse_ids, "index": variant_index},
)

duplicates = get_duplicated_values(warehouse_ids)
if duplicates:
message = "Duplicated warehouse ID."
index_error_map[variant_index].append(
ProductVariantBulkError(
field="stocks",
message=message,
code=ProductVariantBulkErrorCode.DUPLICATED_INPUT_ITEM.value,
warehouses=duplicates,
)
)
if errors is not None:
errors["stocks"] = ValidationError(
message,
"Duplicated warehouse ID.",
code=ProductVariantBulkErrorCode.DUPLICATED_INPUT_ITEM.value,
params={"warehouses": duplicates, "index": variant_index},
)

for stock_data in stocks_data:
if (
stock_data["warehouse"] in duplicates
or stock_data["warehouse"] in wrong_warehouse_ids
):
for stock_index, stock_data in enumerate(stocks_data):
if stock_data["warehouse"] in wrong_warehouse_ids:
index_error_map[variant_index].append(
ProductVariantBulkError(
field="warehouses",
path=f"{path_prefix}.{stock_index}.warehouse",
message="Not existing warehouse ID.",
code=ProductVariantBulkErrorCode.NOT_FOUND.value,
warehouses=[stock_data["warehouse"]],
)
)
continue
else:
stock_data["warehouse"] = warehouse_global_id_to_instance_map[
stock_data["warehouse"]
]
stocks_to_create.append(stock_data)

if stock_data["warehouse"] in duplicates:
index_error_map[variant_index].append(
ProductVariantBulkError(
field="warehouse",
path=f"{path_prefix}.{stock_index}.warehouse",
message="Duplicated warehouse ID.",
code=ProductVariantBulkErrorCode.DUPLICATED_INPUT_ITEM.value,
warehouses=[stock_data["warehouse"]],
)
)
continue

stock_data["warehouse"] = warehouse_global_id_to_instance_map[
stock_data["warehouse"]
]
stocks_to_create.append(stock_data)

return stocks_to_create

Expand All @@ -560,6 +576,7 @@ def add_indexes_to_errors(cls, index, error, error_dict, index_error_map):
index_error_map[index].append(
ProductVariantBulkError(
field=to_camel_case(key),
path=to_camel_case(key),
message=e.messages[0],
code=e.code,
)
Expand Down Expand Up @@ -614,6 +631,7 @@ def validate_base_fields(
index_error_map[index].append(
ProductVariantBulkError(
field="weight",
path="weight",
message=message,
code=code,
)
Expand All @@ -634,6 +652,7 @@ def validate_base_fields(
index_error_map[index].append(
ProductVariantBulkError(
field="quantity_limit_per_customer",
path="quantity_limit_per_customer",
message=message,
code=code,
)
Expand All @@ -649,7 +668,9 @@ def validate_base_fields(
message = "Duplicated SKU."
code = ProductVariantBulkErrorCode.UNIQUE.value
index_error_map[index].append(
ProductVariantBulkError(field="sku", message=message, code=code)
ProductVariantBulkError(
field="sku", path="sku", message=message, code=code
)
)
if errors is not None:
errors["sku"].append(
Expand Down
Loading

0 comments on commit 05df3be

Please sign in to comment.