Repository: saleor/saleor
Pull request: Separate input for ProductCreate and ProductUpdate
I want to merge this change because...

closes #2677 

### Screenshots

<!-- If your changes affect the UI, providing "before" and "after" screenshots will
greatly reduce the amount of work needed to review your work. -->

### Pull Request Checklist

<!-- Please keep this section. It will make maintainer's life easier. -->

1. [x] Privileged views and APIs are guarded by proper permission checks.
1. [x] All visible strings are translated with proper context.
1. [x] All data-formatting is locale-aware (dates, numbers, and so on).
1. [x] Database queries are optimized and the number of queries is constant.
1. [x] The changes are tested.
1. [ ] The code is documented (docstrings, project documentation).


Problem statement:
It's possible to change the product's type using the GraphQL mutation
Currently `productCreate` and `productUpdate` share the same input type.

This causes `productCreate` to list `productType` (an ID field) as optional (not providing it is invalid).

It also causes `productUpdate` to accept a new value for `productType` which should be illegal as it could turn a book into a pair of jeans (and make all product and variant attributes invalid).

Hints from issue discussion:

Commit message: Separate input for ProductCreate and ProductUpdate

Patch to review:
diff --git a/saleor/graphql/product/mutations/products.py b/saleor/graphql/product/mutations/products.py
index 7dad6e2fd00..d8dd5722aca 100644
--- a/saleor/graphql/product/mutations/products.py
+++ b/saleor/graphql/product/mutations/products.py
@@ -196,7 +196,7 @@ class AttributeValueInput(InputObjectType):
         required=True, description='Value of an attribute.')
 
 
-class ProductInput(graphene.InputObjectType):
+class ProductPropertiesInput(graphene.InputObjectType):
     attributes = graphene.List(
         AttributeValueInput,
         description='List of product attributes.')
@@ -214,9 +214,6 @@ class ProductInput(graphene.InputObjectType):
     is_published = graphene.Boolean(
         description='Determines if product is visible to customers.')
     name = graphene.String(description='Product name.')
-    product_type = graphene.ID(
-        description='ID of the type that product belongs to.',
-        name='productType')
     price = Decimal(description='Product price.')
     tax_rate = TaxRateType(description='Tax rate.')
     seo = SeoInput(description='Search engine optimization fields.')
@@ -224,9 +221,15 @@ class ProductInput(graphene.InputObjectType):
         description='Weight of the Product.', required=False)
 
 
+class ProductCreateInput(ProductPropertiesInput):
+    product_type = graphene.ID(
+        description='ID of the type that product belongs to.',
+        name='productType', required=True)
+
+
 class ProductCreate(ModelMutation):
     class Arguments:
-        input = ProductInput(
+        input = ProductCreateInput(
             required=True, description='Fields required to create a product.')
 
     class Meta:
@@ -272,7 +275,7 @@ class ProductUpdate(ProductCreate):
     class Arguments:
         id = graphene.ID(
             required=True, description='ID of a product to update.')
-        input = ProductInput(
+        input = ProductPropertiesInput(
             required=True, description='Fields required to update a product.')
 
     class Meta:


Code review comments:
diff_hunk: @@ -196,7 +196,7 @@ class AttributeValueInput(InputObjectType):
         required=True, description='Value of an attribute.')
 
 
-class ProductInput(graphene.InputObjectType):
+class ProductPropertiesInput(graphene.InputObjectType):
path: saleor/graphql/product/mutations/products.py
text: @user1:
@author I would do a similar change as in your other PR - let's change it here to `ProductInput` and have dedicated `ProductCreateInput` only for product creation.