From bd23c20fed1d50ce45d9261d74dd64124c098628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20M=C3=BCller?= Date: Thu, 26 Oct 2023 13:28:16 +0200 Subject: [PATCH] chore(cart): expose active option (product+qty) for bundle products within the cart items --- CHANGELOG.md | 1 + .../product/dto/productdto_bundleproduct.go | 12 +- product/interfaces/graphql/schema.graphql | 3 +- .../projecttest/graphql/generated.go | 73 ++++++++- ...product_interfaces_graphql-Service.graphql | 3 +- .../projecttest/tests/graphql/cart_test.go | 150 ++++++++++-------- ...mmerce_Cart_UpdateItemBundleConfig.graphql | 9 ++ ...ce_Cart_UpdateItemBundleConfig_one.graphql | 9 ++ .../commerce_cart_AddBundleToCart.graphql | 63 ++++---- 9 files changed, 225 insertions(+), 98 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e51b7f7f7..14b0d1f34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ * **Breaking**(In case you have implemented a custom cart service): Extend the cart service interface with `UpdateItemBundleConfig` to allow updating bundles that have already been placed inside the cart. * GraphQL: * Add new mutation `Commerce_Cart_UpdateItemBundleConfig` to update bundle configs for existing cart items + * Expose Active Option (product+qty) for bundle products within the cart items **checkout** * initialize place order metrics with 0 on application start to follow prometheus best practices diff --git a/product/interfaces/graphql/product/dto/productdto_bundleproduct.go b/product/interfaces/graphql/product/dto/productdto_bundleproduct.go index ba546b4a1..380c4e2c6 100644 --- a/product/interfaces/graphql/product/dto/productdto_bundleproduct.go +++ b/product/interfaces/graphql/product/dto/productdto_bundleproduct.go @@ -15,7 +15,10 @@ type ( Required bool Label string Options []Option - Active Product + + // Deprecated: ActiveOption provides the product and quantity of the active choice + Active Product + ActiveOption Option } Option struct { @@ -128,7 +131,12 @@ func mapWithActiveChoices(domainChoices []productDomain.Choice, activeChoices ma for i, choice := range choices { activeChoice, ok := activeChoices[productDomain.Identifier(choice.Identifier)] if ok { - choices[i].Active = NewGraphqlProductDto(activeChoice.Product, nil, nil) + product := NewGraphqlProductDto(activeChoice.Product, nil, nil) + choices[i].Active = product + choices[i].ActiveOption = Option{ + Product: product, + Qty: activeChoice.Qty, + } } } diff --git a/product/interfaces/graphql/schema.graphql b/product/interfaces/graphql/schema.graphql index 979e831ad..f305f09ba 100644 --- a/product/interfaces/graphql/schema.graphql +++ b/product/interfaces/graphql/schema.graphql @@ -319,7 +319,8 @@ type Commerce_Product_Choice { required: Boolean! label: String! options: [Commerce_Product_Option!] - active: Commerce_Product + active: Commerce_Product @deprecated(reason: "use activeOption instead") + activeOption: Commerce_Product_Option } type Commerce_Product_Option { diff --git a/test/integrationtest/projecttest/graphql/generated.go b/test/integrationtest/projecttest/graphql/generated.go index 29879fe57..439fa70a2 100644 --- a/test/integrationtest/projecttest/graphql/generated.go +++ b/test/integrationtest/projecttest/graphql/generated.go @@ -750,11 +750,12 @@ type ComplexityRoot struct { } Commerce_Product_Choice struct { - Active func(childComplexity int) int - Identifier func(childComplexity int) int - Label func(childComplexity int) int - Options func(childComplexity int) int - Required func(childComplexity int) int + Active func(childComplexity int) int + ActiveOption func(childComplexity int) int + Identifier func(childComplexity int) int + Label func(childComplexity int) int + Options func(childComplexity int) int + Required func(childComplexity int) int } Commerce_Product_ConfigurableProduct struct { @@ -4136,6 +4137,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Commerce_Product_Choice.Active(childComplexity), true + case "Commerce_Product_Choice.activeOption": + if e.complexity.Commerce_Product_Choice.ActiveOption == nil { + break + } + + return e.complexity.Commerce_Product_Choice.ActiveOption(childComplexity), true + case "Commerce_Product_Choice.identifier": if e.complexity.Commerce_Product_Choice.Identifier == nil { break @@ -26037,6 +26045,8 @@ func (ec *executionContext) fieldContext_Commerce_Product_BundleProduct_choices( return ec.fieldContext_Commerce_Product_Choice_options(ctx, field) case "active": return ec.fieldContext_Commerce_Product_Choice_active(ctx, field) + case "activeOption": + return ec.fieldContext_Commerce_Product_Choice_activeOption(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type Commerce_Product_Choice", field.Name) }, @@ -26552,6 +26562,53 @@ func (ec *executionContext) fieldContext_Commerce_Product_Choice_active(ctx cont return fc, nil } +func (ec *executionContext) _Commerce_Product_Choice_activeOption(ctx context.Context, field graphql.CollectedField, obj *graphqlproductdto.Choice) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Commerce_Product_Choice_activeOption(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ActiveOption, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(graphqlproductdto.Option) + fc.Result = res + return ec.marshalOCommerce_Product_Option2flamingoᚗmeᚋflamingoᚑcommerceᚋv3ᚋproductᚋinterfacesᚋgraphqlᚋproductᚋdtoᚐOption(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Commerce_Product_Choice_activeOption(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Commerce_Product_Choice", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "product": + return ec.fieldContext_Commerce_Product_Option_product(ctx, field) + case "qty": + return ec.fieldContext_Commerce_Product_Option_qty(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Commerce_Product_Option", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _Commerce_Product_ConfigurableProduct_type(ctx context.Context, field graphql.CollectedField, obj *graphqlproductdto.ConfigurableProduct) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Commerce_Product_ConfigurableProduct_type(ctx, field) if err != nil { @@ -43286,6 +43343,8 @@ func (ec *executionContext) _Commerce_Product_Choice(ctx context.Context, sel as out.Values[i] = ec._Commerce_Product_Choice_options(ctx, field, obj) case "active": out.Values[i] = ec._Commerce_Product_Choice_active(ctx, field, obj) + case "activeOption": + out.Values[i] = ec._Commerce_Product_Choice_activeOption(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -49232,6 +49291,10 @@ func (ec *executionContext) marshalOCommerce_Product_MediaItem2ᚕflamingoᚗme return ret } +func (ec *executionContext) marshalOCommerce_Product_Option2flamingoᚗmeᚋflamingoᚑcommerceᚋv3ᚋproductᚋinterfacesᚋgraphqlᚋproductᚋdtoᚐOption(ctx context.Context, sel ast.SelectionSet, v graphqlproductdto.Option) graphql.Marshaler { + return ec._Commerce_Product_Option(ctx, sel, &v) +} + func (ec *executionContext) marshalOCommerce_Product_Option2ᚕflamingoᚗmeᚋflamingoᚑcommerceᚋv3ᚋproductᚋinterfacesᚋgraphqlᚋproductᚋdtoᚐOptionᚄ(ctx context.Context, sel ast.SelectionSet, v []graphqlproductdto.Option) graphql.Marshaler { if v == nil { return graphql.Null diff --git a/test/integrationtest/projecttest/graphql/schema/flamingo.me_flamingo-commerce_v3_product_interfaces_graphql-Service.graphql b/test/integrationtest/projecttest/graphql/schema/flamingo.me_flamingo-commerce_v3_product_interfaces_graphql-Service.graphql index 979e831ad..f305f09ba 100644 --- a/test/integrationtest/projecttest/graphql/schema/flamingo.me_flamingo-commerce_v3_product_interfaces_graphql-Service.graphql +++ b/test/integrationtest/projecttest/graphql/schema/flamingo.me_flamingo-commerce_v3_product_interfaces_graphql-Service.graphql @@ -319,7 +319,8 @@ type Commerce_Product_Choice { required: Boolean! label: String! options: [Commerce_Product_Option!] - active: Commerce_Product + active: Commerce_Product @deprecated(reason: "use activeOption instead") + activeOption: Commerce_Product_Option } type Commerce_Product_Option { diff --git a/test/integrationtest/projecttest/tests/graphql/cart_test.go b/test/integrationtest/projecttest/tests/graphql/cart_test.go index 5402843b6..13eed3c50 100644 --- a/test/integrationtest/projecttest/tests/graphql/cart_test.go +++ b/test/integrationtest/projecttest/tests/graphql/cart_test.go @@ -223,37 +223,50 @@ func TestAddBundleProductToCart(t *testing.T) { body := response.Expect().Body() expected := `{ - "data": { - "Commerce_Cart_AddToCart": { - "decoratedDeliveries": [ - { - "decoratedItems": [ - { - "product": { - "marketPlaceCode": "fake_bundle", - "choices": [ - { - "identifier": "identifier1", - "active": { - "marketPlaceCode": "simple_option1" - } - }, - { - "identifier": "identifier2", - "active": { - "marketPlaceCode": "configurable_option2", - "variantMarketPlaceCode": "shirt-red-s" - } - } - ] - } - } - ] - } - ] - } - } - }` + "data": { + "Commerce_Cart_AddToCart": { + "decoratedDeliveries": [ + { + "decoratedItems": [ + { + "product": { + "marketPlaceCode": "fake_bundle", + "choices": [ + { + "identifier": "identifier1", + "active": { + "marketPlaceCode": "simple_option1" + }, + "activeOption": { + "product": { + "marketPlaceCode": "simple_option1" + }, + "qty": 1 + } + }, + { + "identifier": "identifier2", + "active": { + "marketPlaceCode": "configurable_option2", + "variantMarketPlaceCode": "shirt-red-s" + }, + "activeOption": { + "product": { + "marketPlaceCode": "configurable_option2", + "variantMarketPlaceCode": "shirt-red-s" + }, + "qty": 1 + } + } + ] + } + } + ] + } + ] + } + } +}` expected = spaceMap(expected) body.IsEqual(expected) @@ -395,37 +408,50 @@ func TestUpdateBundleConfiguration(t *testing.T) { body := updateResponse.Expect().Body() expected := `{ - "data": { - "Commerce_Cart_UpdateItemBundleConfig": { - "decoratedDeliveries": [ - { - "decoratedItems": [ - { - "product": { - "marketPlaceCode": "fake_bundle", - "choices": [ - { - "identifier": "identifier1", - "active": { - "marketPlaceCode": "simple_option2" - } - }, - { - "identifier": "identifier2", - "active": { - "marketPlaceCode": "configurable_option1", - "variantMarketPlaceCode": "shirt-red-m" - } - } - ] - } - } - ] - } - ] - } - } - }` + "data": { + "Commerce_Cart_UpdateItemBundleConfig": { + "decoratedDeliveries": [ + { + "decoratedItems": [ + { + "product": { + "marketPlaceCode": "fake_bundle", + "choices": [ + { + "identifier": "identifier1", + "active": { + "marketPlaceCode": "simple_option2" + }, + "activeOption": { + "product": { + "marketPlaceCode": "simple_option2" + }, + "qty": 1 + } + }, + { + "identifier": "identifier2", + "active": { + "marketPlaceCode": "configurable_option1", + "variantMarketPlaceCode": "shirt-red-m" + }, + "activeOption": { + "product": { + "marketPlaceCode": "configurable_option1", + "variantMarketPlaceCode": "shirt-red-m" + }, + "qty": 1 + } + } + ] + } + } + ] + } + ] + } + } +}` expected = spaceMap(expected) body.IsEqual(expected) diff --git a/test/integrationtest/projecttest/tests/graphql/testdata/Commerce_Cart_UpdateItemBundleConfig.graphql b/test/integrationtest/projecttest/tests/graphql/testdata/Commerce_Cart_UpdateItemBundleConfig.graphql index 7594a5e0c..402daa3c3 100644 --- a/test/integrationtest/projecttest/tests/graphql/testdata/Commerce_Cart_UpdateItemBundleConfig.graphql +++ b/test/integrationtest/projecttest/tests/graphql/testdata/Commerce_Cart_UpdateItemBundleConfig.graphql @@ -26,6 +26,15 @@ mutation { variantMarketPlaceCode } } + activeOption { + product { + marketPlaceCode + ... on Commerce_Product_ActiveVariantProduct { + variantMarketPlaceCode + } + } + qty + } } } } diff --git a/test/integrationtest/projecttest/tests/graphql/testdata/Commerce_Cart_UpdateItemBundleConfig_one.graphql b/test/integrationtest/projecttest/tests/graphql/testdata/Commerce_Cart_UpdateItemBundleConfig_one.graphql index e2fdad1e9..82a8ae91b 100644 --- a/test/integrationtest/projecttest/tests/graphql/testdata/Commerce_Cart_UpdateItemBundleConfig_one.graphql +++ b/test/integrationtest/projecttest/tests/graphql/testdata/Commerce_Cart_UpdateItemBundleConfig_one.graphql @@ -21,6 +21,15 @@ mutation { variantMarketPlaceCode } } + activeOption { + product { + marketPlaceCode + ... on Commerce_Product_ActiveVariantProduct { + variantMarketPlaceCode + } + } + qty + } } } } diff --git a/test/integrationtest/projecttest/tests/graphql/testdata/commerce_cart_AddBundleToCart.graphql b/test/integrationtest/projecttest/tests/graphql/testdata/commerce_cart_AddBundleToCart.graphql index 0bef6f2b2..20950a446 100644 --- a/test/integrationtest/projecttest/tests/graphql/testdata/commerce_cart_AddBundleToCart.graphql +++ b/test/integrationtest/projecttest/tests/graphql/testdata/commerce_cart_AddBundleToCart.graphql @@ -1,37 +1,46 @@ mutation { Commerce_Cart_AddToCart( - addToCartInput: { - marketplaceCode: "###MARKETPLACE_CODE###" - qty: 1 - deliveryCode: "###DELIVERY_CODE###" - bundleConfiguration: [{ - identifier: "###IDENTIFIER1###" - marketplaceCode: "###MARKETPLACE_CODE1###" - variantMarketplaceCode: "###VARIANT_MARKETPLACE_CODE1###" - },{ - identifier: "###IDENTIFIER2###" - marketplaceCode: "###MARKETPLACE_CODE2###" - variantMarketplaceCode: "###VARIANT_MARKETPLACE_CODE2###" - }] - } + addToCartInput: { + marketplaceCode: "###MARKETPLACE_CODE###" + qty: 1 + deliveryCode: "###DELIVERY_CODE###" + bundleConfiguration: [{ + identifier: "###IDENTIFIER1###" + marketplaceCode: "###MARKETPLACE_CODE1###" + variantMarketplaceCode: "###VARIANT_MARKETPLACE_CODE1###" + },{ + identifier: "###IDENTIFIER2###" + marketplaceCode: "###MARKETPLACE_CODE2###" + variantMarketplaceCode: "###VARIANT_MARKETPLACE_CODE2###" + }] + } ) { decoratedDeliveries { - decoratedItems { - product { - marketPlaceCode - ... on Commerce_Product_BundleProduct { - choices { - identifier - active { + decoratedItems { + product { marketPlaceCode - ... on Commerce_Product_ActiveVariantProduct { - variantMarketPlaceCode + ... on Commerce_Product_BundleProduct { + choices { + identifier + active { + marketPlaceCode + ... on Commerce_Product_ActiveVariantProduct { + variantMarketPlaceCode + } + } + activeOption { + product { + marketPlaceCode + ... on Commerce_Product_ActiveVariantProduct { + variantMarketPlaceCode + } + } + qty + } + } } - } } - } } - } } - } + } }