From b9e28fd3dc2b32a03984f4f62fc5cf612842932f Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Mon, 13 Apr 2020 17:11:29 +0200 Subject: [PATCH 01/37] Single mutation for adding items to the shopping cart --- .../Quote/Model/Cart/AddProductsToCart.php | 214 ++++ .../Model/Cart/AddProductsToCartInterface.php | 28 + .../Cart/BuyRequest/BuyRequestBuilder.php | 61 ++ .../BuyRequestDataProviderInterface.php | 24 + .../CustomizableOptionDataProvider.php | 103 ++ .../Cart/Data/AddProductsToCartOutput.php | 56 + .../Quote/Model/Cart/Data/CartItem.php | 110 ++ .../Quote/Model/Cart/Data/CartItemFactory.php | 74 ++ .../Quote/Model/Cart/Data/EnteredOption.php | 51 + .../Magento/Quote/Model/Cart/Data/Error.php | 71 ++ .../Quote/Model/Cart/Data/SelectedOption.php | 34 + app/code/Magento/Quote/etc/graphql/di.xml | 16 + .../Cart/BuyRequest/BundleDataProvider.php | 76 ++ app/code/Magento/QuoteBundleOptions/README.md | 3 + .../Magento/QuoteBundleOptions/composer.json | 22 + .../QuoteBundleOptions/etc/graphql/di.xml | 16 + .../Magento/QuoteBundleOptions/etc/module.xml | 10 + .../QuoteBundleOptions/registration.php | 10 + .../BuyRequest/SuperAttributeDataProvider.php | 74 ++ .../QuoteConfigurableOptions/README.md | 3 + .../QuoteConfigurableOptions/composer.json | 22 + .../etc/graphql/di.xml | 16 + .../QuoteConfigurableOptions/etc/module.xml | 10 + .../QuoteConfigurableOptions/registration.php | 10 + .../DownloadableLinkDataProvider.php | 75 ++ .../Magento/QuoteDownloadableLinks/README.md | 3 + .../QuoteDownloadableLinks/composer.json | 22 + .../QuoteDownloadableLinks/etc/graphql/di.xml | 16 + .../QuoteDownloadableLinks/etc/module.xml | 10 + .../QuoteDownloadableLinks/registration.php | 10 + .../Model/Resolver/AddProductsToCart.php | 93 ++ .../Magento/QuoteGraphQl/etc/schema.graphqls | 28 + .../Magento/SalesGraphQl/etc/schema.graphqls | 10 - composer.json | 3 + composer.lock | 958 ++++++++++++------ ...dBundleProductToCartSingleMutationTest.php | 225 ++++ ...gurableProductToCartSingleMutationTest.php | 292 ++++++ ...oadableProductToCartSingleMutationTest.php | 211 ++++ .../GetCustomOptionsWithIDV2ForQueryBySku.php | 94 ++ vendor/.htaccess | 7 - 40 files changed, 2830 insertions(+), 341 deletions(-) create mode 100644 app/code/Magento/Quote/Model/Cart/AddProductsToCart.php create mode 100644 app/code/Magento/Quote/Model/Cart/AddProductsToCartInterface.php create mode 100644 app/code/Magento/Quote/Model/Cart/BuyRequest/BuyRequestBuilder.php create mode 100644 app/code/Magento/Quote/Model/Cart/BuyRequest/BuyRequestDataProviderInterface.php create mode 100644 app/code/Magento/Quote/Model/Cart/BuyRequest/CustomizableOptionDataProvider.php create mode 100644 app/code/Magento/Quote/Model/Cart/Data/AddProductsToCartOutput.php create mode 100644 app/code/Magento/Quote/Model/Cart/Data/CartItem.php create mode 100644 app/code/Magento/Quote/Model/Cart/Data/CartItemFactory.php create mode 100644 app/code/Magento/Quote/Model/Cart/Data/EnteredOption.php create mode 100644 app/code/Magento/Quote/Model/Cart/Data/Error.php create mode 100644 app/code/Magento/Quote/Model/Cart/Data/SelectedOption.php create mode 100644 app/code/Magento/Quote/etc/graphql/di.xml create mode 100644 app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php create mode 100644 app/code/Magento/QuoteBundleOptions/README.md create mode 100644 app/code/Magento/QuoteBundleOptions/composer.json create mode 100644 app/code/Magento/QuoteBundleOptions/etc/graphql/di.xml create mode 100644 app/code/Magento/QuoteBundleOptions/etc/module.xml create mode 100644 app/code/Magento/QuoteBundleOptions/registration.php create mode 100644 app/code/Magento/QuoteConfigurableOptions/Model/Cart/BuyRequest/SuperAttributeDataProvider.php create mode 100644 app/code/Magento/QuoteConfigurableOptions/README.md create mode 100644 app/code/Magento/QuoteConfigurableOptions/composer.json create mode 100644 app/code/Magento/QuoteConfigurableOptions/etc/graphql/di.xml create mode 100644 app/code/Magento/QuoteConfigurableOptions/etc/module.xml create mode 100644 app/code/Magento/QuoteConfigurableOptions/registration.php create mode 100644 app/code/Magento/QuoteDownloadableLinks/Model/Cart/BuyRequest/DownloadableLinkDataProvider.php create mode 100644 app/code/Magento/QuoteDownloadableLinks/README.md create mode 100644 app/code/Magento/QuoteDownloadableLinks/composer.json create mode 100644 app/code/Magento/QuoteDownloadableLinks/etc/graphql/di.xml create mode 100644 app/code/Magento/QuoteDownloadableLinks/etc/module.xml create mode 100644 app/code/Magento/QuoteDownloadableLinks/registration.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/AddProductsToCart.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddDownloadableProductToCartSingleMutationTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCustomOptionsWithIDV2ForQueryBySku.php delete mode 100644 vendor/.htaccess diff --git a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php new file mode 100644 index 0000000000000..5d7eb053dc892 --- /dev/null +++ b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php @@ -0,0 +1,214 @@ + self::ERROR_NOT_SALABLE, + 'Product that you are trying to add is not available' => self::ERROR_NOT_SALABLE, + 'This product is out of stock' => self::ERROR_NOT_SALABLE, + 'There are no source items' => self::ERROR_NOT_SALABLE, + 'The fewest you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, + 'The most you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, + 'The requested qty is not available' => self::ERROR_INSUFFICIENT_STOCK, + ]; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var array + */ + private $errors = []; + + /** + * @var CartRepositoryInterface + */ + private $cartRepository; + + /** + * @var MaskedQuoteIdToQuoteIdInterface + */ + private $maskedQuoteIdToQuoteId; + + /** + * @var BuyRequestBuilder + */ + private $requestBuilder; + + /** + * @param ProductRepositoryInterface $productRepository + * @param CartRepositoryInterface $cartRepository + * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId + * @param BuyRequestBuilder $requestBuilder + */ + public function __construct( + ProductRepositoryInterface $productRepository, + CartRepositoryInterface $cartRepository, + MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, + BuyRequestBuilder $requestBuilder + ) { + $this->productRepository = $productRepository; + $this->cartRepository = $cartRepository; + $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; + $this->requestBuilder = $requestBuilder; + } + + /** + * @inheritdoc + */ + public function execute(string $maskedCartId, array $cartItems): AddProductsToCartOutput + { + $cartId = $this->maskedQuoteIdToQuoteId->execute($maskedCartId); + $cart = $this->cartRepository->get($cartId); + + foreach ($cartItems as $n => $cartItem) { + $this->addItemToCart($cart, $cartItem, $n); + } + if ($cart->getData('has_error')) { + $errors = $cart->getErrors(); + + /** @var MessageInterface $error */ + foreach ($errors as $error) { + $this->addError($error->getText()); + } + } + $this->cartRepository->save($cart); + + return $this->prepareErrorOutput($cart); + } + + /** + * Adds a particular item to the shopping cart + * + * @param CartInterface|Quote $cart + * @param Data\CartItem $cartItem + * @param int $cartItemPosition + */ + private function addItemToCart(CartInterface $cart, Data\CartItem $cartItem, int $cartItemPosition): void + { + $sku = $cartItem->getSku(); + + try { + $product = $this->productRepository->get($sku, false, null, true); + } catch (NoSuchEntityException $e) { + $this->addError( + __('Could not find a product with SKU "%sku"', ['sku' => $sku])->render(), + $cartItemPosition, + self::ERROR_PRODUCT_NOT_FOUND + ); + + return; + } + + try { + $result = $cart->addProduct($product, $this->requestBuilder->build($cartItem)); + } catch (\Throwable $e) { + $this->addError( + __( + 'Could not add the product with SKU %sku to the shopping cart: %message', + ['sku' => $sku, 'message' => $e->getMessage()] + )->render(), + $cartItemPosition + ); + return; + } + + if (is_string($result)) { + $errors = array_unique(explode("\n", $result)); + foreach ($errors as $error) { + $this->addError(__($error)->render(), $cartItemPosition); + } + } + } + + /** + * Add order line item error + * + * @param string $message + * @param int $cartItemPosition + * @param string|null $code + * @return void + */ + private function addError(string $message, int $cartItemPosition = 0, string $code = ''): void + { + $this->errors[] = new Data\Error( + $message, + $code ?? $this->getErrorCode($message), + $cartItemPosition + ); + } + + /** + * Get message error code. + * + * @param string $message + * @return string + */ + private function getErrorCode(string $message): string + { + $code = self::ERROR_UNDEFINED; + + $matchedCodes = array_filter( + self::MESSAGE_CODES, + function ($key) use ($message) { + return false !== strpos($message, $key); + }, + ARRAY_FILTER_USE_KEY + ); + + if (!empty($matchedCodes)) { + $code = current($matchedCodes); + } + + return $code; + } + + /** + * Creates a new output from existing errors + * + * @param CartInterface $cart + * @return AddProductsToCartOutput + */ + private function prepareErrorOutput(CartInterface $cart): AddProductsToCartOutput + { + $output = new AddProductsToCartOutput($cart, $this->errors); + $this->errors = []; + $cart->setHasError(false); + + return $output; + } +} diff --git a/app/code/Magento/Quote/Model/Cart/AddProductsToCartInterface.php b/app/code/Magento/Quote/Model/Cart/AddProductsToCartInterface.php new file mode 100644 index 0000000000000..98e69af2a0179 --- /dev/null +++ b/app/code/Magento/Quote/Model/Cart/AddProductsToCartInterface.php @@ -0,0 +1,28 @@ +dataObjectFactory = $dataObjectFactory; + $this->providers = $providers; + } + + /** + * Build buy request for adding product to cart + * + * @see \Magento\Quote\Model\Quote::addProduct + * @param CartItem $cartItem + * @return DataObject + */ + public function build(CartItem $cartItem): DataObject + { + $requestData = [ + ['qty' => $cartItem->getQuantity()] + ]; + + /** @var BuyRequestDataProviderInterface $provider */ + foreach ($this->providers as $provider) { + $requestData[] = $provider->execute($cartItem); + } + + return $this->dataObjectFactory->create(['data' => array_merge(...$requestData)]); + } +} diff --git a/app/code/Magento/Quote/Model/Cart/BuyRequest/BuyRequestDataProviderInterface.php b/app/code/Magento/Quote/Model/Cart/BuyRequest/BuyRequestDataProviderInterface.php new file mode 100644 index 0000000000000..b9c41b18ee163 --- /dev/null +++ b/app/code/Magento/Quote/Model/Cart/BuyRequest/BuyRequestDataProviderInterface.php @@ -0,0 +1,24 @@ +getSelectedOptions() as $optionData) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $optionData = \explode('/', base64_decode($optionData->getId())); + + if ($this->isProviderApplicable($optionData) === false) { + continue; + } + $this->validateInput($optionData); + + [, $optionId, $optionValue] = $optionData; + $customizableOptionsData[$optionId][] = $optionValue; + } + + foreach ($cartItem->getEnteredOptions() as $option) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $optionData = \explode('/', base64_decode($option->getId())); + + if ($this->isProviderApplicable($optionData) === false) { + continue; + } + + [, $optionId] = $optionData; + $customizableOptionsData[$optionId][] = $option->getValue(); + } + + return ['options' => $this->flattenOptionValues($customizableOptionsData)]; + } + + /** + * Flatten option values for non-multiselect customizable options + * + * @param array $customizableOptionsData + * @return array + */ + private function flattenOptionValues(array $customizableOptionsData): array + { + foreach ($customizableOptionsData as $optionId => $optionValue) { + if (count($optionValue) === 1) { + $customizableOptionsData[$optionId] = $optionValue[0]; + } + } + + return $customizableOptionsData; + } + + /** + * Checks whether this provider is applicable for the current option + * + * @param array $optionData + * @return bool + */ + private function isProviderApplicable(array $optionData): bool + { + if ($optionData[0] !== self::OPTION_TYPE) { + return false; + } + + return true; + } + + /** + * Validates the provided options structure + * + * @param array $optionData + * @throws LocalizedException + */ + private function validateInput(array $optionData): void + { + if (count($optionData) !== 3) { + throw new LocalizedException( + __('Wrong format of the entered option data. Must be "custom-option/option_id/option_value_id"') + ); + } + } +} diff --git a/app/code/Magento/Quote/Model/Cart/Data/AddProductsToCartOutput.php b/app/code/Magento/Quote/Model/Cart/Data/AddProductsToCartOutput.php new file mode 100644 index 0000000000000..6088b12a36e7c --- /dev/null +++ b/app/code/Magento/Quote/Model/Cart/Data/AddProductsToCartOutput.php @@ -0,0 +1,56 @@ +cart = $cart; + $this->errors = $errors; + } + + /** + * Get Shopping Cart + * + * @return CartInterface + */ + public function getCart(): CartInterface + { + return $this->cart; + } + + /** + * Get errors happened during reorder + * + * @return Error[] + */ + public function getErrors(): array + { + return $this->errors; + } +} diff --git a/app/code/Magento/Quote/Model/Cart/Data/CartItem.php b/app/code/Magento/Quote/Model/Cart/Data/CartItem.php new file mode 100644 index 0000000000000..9836247c56694 --- /dev/null +++ b/app/code/Magento/Quote/Model/Cart/Data/CartItem.php @@ -0,0 +1,110 @@ +sku = $sku; + $this->quantity = $quantity; + $this->parentSku = $parentSku; + $this->selectedOptions = $selectedOptions; + $this->enteredOptions = $enteredOptions; + } + + /** + * Returns cart item SKU + * + * @return string + */ + public function getSku(): string + { + return $this->sku; + } + + /** + * Returns cart item quantity + * + * @return float + */ + public function getQuantity(): float + { + return $this->quantity; + } + + /** + * Returns parent SKU + * + * @return string|null + */ + public function getParentSku(): ?string + { + return $this->parentSku; + } + + /** + * Returns selected options + * + * @return SelectedOption[]|null + */ + public function getSelectedOptions(): ?array + { + return $this->selectedOptions; + } + + /** + * Returns entered options + * + * @return EnteredOption[]|null + */ + public function getEnteredOptions(): ?array + { + return $this->enteredOptions; + } +} diff --git a/app/code/Magento/Quote/Model/Cart/Data/CartItemFactory.php b/app/code/Magento/Quote/Model/Cart/Data/CartItemFactory.php new file mode 100644 index 0000000000000..468d5950bd2dd --- /dev/null +++ b/app/code/Magento/Quote/Model/Cart/Data/CartItemFactory.php @@ -0,0 +1,74 @@ +createSelectedOptions($data['selected_options']) : [], + isset($data['entered_options']) ? $this->createEnteredOptions($data['entered_options']) : [] + ); + } + + /** + * Creates array of Entered Options + * + * @param array $options + * @return EnteredOption[] + */ + private function createEnteredOptions(array $options): array + { + return \array_map( + function (array $option) { + if (!isset($option['id'], $option['value'])) { + throw new InputException( + __('Required fields are not present EnteredOption.id, EnteredOption.value') + ); + } + return new EnteredOption($option['id'], $option['value']); + }, + $options + ); + } + + /** + * Creates array of Selected Options + * + * @param string[] $options + * @return SelectedOption[] + */ + private function createSelectedOptions(array $options): array + { + return \array_map( + function ($option) { + return new SelectedOption($option); + }, + $options + ); + } +} diff --git a/app/code/Magento/Quote/Model/Cart/Data/EnteredOption.php b/app/code/Magento/Quote/Model/Cart/Data/EnteredOption.php new file mode 100644 index 0000000000000..f11aa49d6b4c3 --- /dev/null +++ b/app/code/Magento/Quote/Model/Cart/Data/EnteredOption.php @@ -0,0 +1,51 @@ +id = $id; + $this->value = $value; + } + + /** + * Returns entered option ID + * + * @return string + */ + public function getId(): string + { + return $this->id; + } + + /** + * Returns entered option value + * + * @return string + */ + public function getValue(): string + { + return $this->value; + } +} diff --git a/app/code/Magento/Quote/Model/Cart/Data/Error.php b/app/code/Magento/Quote/Model/Cart/Data/Error.php new file mode 100644 index 0000000000000..42b14b06d94aa --- /dev/null +++ b/app/code/Magento/Quote/Model/Cart/Data/Error.php @@ -0,0 +1,71 @@ +message = $message; + $this->code = $code; + $this->cartItemPosition = $cartItemPosition; + } + + /** + * Get error message + * + * @return string + */ + public function getMessage(): string + { + return $this->message; + } + + /** + * Get error code + * + * @return string + */ + public function getCode(): string + { + return $this->code; + } + + /** + * Get cart item position + * + * @return int + */ + public function getCartItemPosition(): int + { + return $this->cartItemPosition; + } +} diff --git a/app/code/Magento/Quote/Model/Cart/Data/SelectedOption.php b/app/code/Magento/Quote/Model/Cart/Data/SelectedOption.php new file mode 100644 index 0000000000000..8a15fb3e0dcb4 --- /dev/null +++ b/app/code/Magento/Quote/Model/Cart/Data/SelectedOption.php @@ -0,0 +1,34 @@ +id = $id; + } + + /** + * Get selected option ID + * + * @return string + */ + public function getId(): string + { + return $this->id; + } +} diff --git a/app/code/Magento/Quote/etc/graphql/di.xml b/app/code/Magento/Quote/etc/graphql/di.xml new file mode 100644 index 0000000000000..0e688d42ecb32 --- /dev/null +++ b/app/code/Magento/Quote/etc/graphql/di.xml @@ -0,0 +1,16 @@ + + + + + + + Magento\Quote\Model\Cart\BuyRequest\CustomizableOptionDataProvider + + + + diff --git a/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php b/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php new file mode 100644 index 0000000000000..946e30ce3e276 --- /dev/null +++ b/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php @@ -0,0 +1,76 @@ +getSelectedOptions() as $optionData) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $optionData = \explode('/', base64_decode($optionData->getId())); + + if ($this->isProviderApplicable($optionData) === false) { + continue; + } + $this->validateInput($optionData); + + [, $optionId, $optionValueId, $optionQuantity] = $optionData; + $bundleOptionsData['bundle_option'][$optionId] = $optionValueId; + $bundleOptionsData['bundle_option_qty'][$optionId] = $optionQuantity; + } + + return $bundleOptionsData; + } + + /** + * Checks whether this provider is applicable for the current option + * + * @param array $optionData + * @return bool + */ + private function isProviderApplicable(array $optionData): bool + { + if ($optionData[0] !== self::OPTION_TYPE) { + return false; + } + + return true; + } + + /** + * Validates the provided options structure + * + * @param array $optionData + * @throws LocalizedException + */ + private function validateInput(array $optionData): void + { + if (count($optionData) !== 4) { + $errorMessage = __('Wrong format of the entered option data. ' . + 'Must be "bundle/option_id/option_value_id/option_quantity"'); + throw new LocalizedException($errorMessage); + } + } +} diff --git a/app/code/Magento/QuoteBundleOptions/README.md b/app/code/Magento/QuoteBundleOptions/README.md new file mode 100644 index 0000000000000..3207eeaf2b683 --- /dev/null +++ b/app/code/Magento/QuoteBundleOptions/README.md @@ -0,0 +1,3 @@ +# QuoteBundleOptions + +**QuoteBundleOptions** provides data provider for creating buy request for bundle products. diff --git a/app/code/Magento/QuoteBundleOptions/composer.json b/app/code/Magento/QuoteBundleOptions/composer.json new file mode 100644 index 0000000000000..a26502c5a5d6c --- /dev/null +++ b/app/code/Magento/QuoteBundleOptions/composer.json @@ -0,0 +1,22 @@ +{ + "name": "magento/module-quote-bundle-options", + "description": "N/A", + "type": "magento2-module", + "require": { + "php": "~7.1.3||~7.2.0||~7.3.0", + "magento/framework": "*", + "magento/module-quote": "*" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\QuoteBundleOptions\\": "" + } + } +} diff --git a/app/code/Magento/QuoteBundleOptions/etc/graphql/di.xml b/app/code/Magento/QuoteBundleOptions/etc/graphql/di.xml new file mode 100644 index 0000000000000..e15493e092e3b --- /dev/null +++ b/app/code/Magento/QuoteBundleOptions/etc/graphql/di.xml @@ -0,0 +1,16 @@ + + + + + + + Magento\QuoteBundleOptions\Model\Cart\BuyRequest\BundleDataProvider + + + + diff --git a/app/code/Magento/QuoteBundleOptions/etc/module.xml b/app/code/Magento/QuoteBundleOptions/etc/module.xml new file mode 100644 index 0000000000000..4dc531b561115 --- /dev/null +++ b/app/code/Magento/QuoteBundleOptions/etc/module.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/code/Magento/QuoteBundleOptions/registration.php b/app/code/Magento/QuoteBundleOptions/registration.php new file mode 100644 index 0000000000000..cf4c92fd929d9 --- /dev/null +++ b/app/code/Magento/QuoteBundleOptions/registration.php @@ -0,0 +1,10 @@ +getSelectedOptions() as $optionData) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $optionData = \explode('/', base64_decode($optionData->getId())); + + if ($this->isProviderApplicable($optionData) === false) { + continue; + } + $this->validateInput($optionData); + + [, $attributeId, $valueIndex] = $optionData; + $configurableProductData[$attributeId] = $valueIndex; + } + + return ['super_attribute' => $configurableProductData]; + } + + /** + * Checks whether this provider is applicable for the current option + * + * @param array $optionData + * @return bool + */ + private function isProviderApplicable(array $optionData): bool + { + if ($optionData[0] !== self::OPTION_TYPE) { + return false; + } + + return true; + } + + /** + * Validates the provided options structure + * + * @param array $optionData + * @throws LocalizedException + */ + private function validateInput(array $optionData): void + { + if (count($optionData) !== 3) { + throw new LocalizedException( + __('Wrong format of the entered option data. Must be "configurable/attribute_id/value_index"') + ); + } + } +} diff --git a/app/code/Magento/QuoteConfigurableOptions/README.md b/app/code/Magento/QuoteConfigurableOptions/README.md new file mode 100644 index 0000000000000..db47e2c37c3ff --- /dev/null +++ b/app/code/Magento/QuoteConfigurableOptions/README.md @@ -0,0 +1,3 @@ +# QuoteConfigurableOptions + +**QuoteConfigurableOptions** provides data provider for creating buy request for configurable products. diff --git a/app/code/Magento/QuoteConfigurableOptions/composer.json b/app/code/Magento/QuoteConfigurableOptions/composer.json new file mode 100644 index 0000000000000..1221068c3061e --- /dev/null +++ b/app/code/Magento/QuoteConfigurableOptions/composer.json @@ -0,0 +1,22 @@ +{ + "name": "magento/module-quote-configurable-options", + "description": "N/A", + "type": "magento2-module", + "require": { + "php": "~7.1.3||~7.2.0||~7.3.0", + "magento/framework": "*", + "magento/module-quote": "*" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\QuoteConfigurableOptions\\": "" + } + } +} diff --git a/app/code/Magento/QuoteConfigurableOptions/etc/graphql/di.xml b/app/code/Magento/QuoteConfigurableOptions/etc/graphql/di.xml new file mode 100644 index 0000000000000..c4fe6357a5689 --- /dev/null +++ b/app/code/Magento/QuoteConfigurableOptions/etc/graphql/di.xml @@ -0,0 +1,16 @@ + + + + + + + Magento\QuoteConfigurableOptions\Model\Cart\BuyRequest\SuperAttributeDataProvider + + + + diff --git a/app/code/Magento/QuoteConfigurableOptions/etc/module.xml b/app/code/Magento/QuoteConfigurableOptions/etc/module.xml new file mode 100644 index 0000000000000..e32489c1b2109 --- /dev/null +++ b/app/code/Magento/QuoteConfigurableOptions/etc/module.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/code/Magento/QuoteConfigurableOptions/registration.php b/app/code/Magento/QuoteConfigurableOptions/registration.php new file mode 100644 index 0000000000000..0b55a18a81fce --- /dev/null +++ b/app/code/Magento/QuoteConfigurableOptions/registration.php @@ -0,0 +1,10 @@ +getSelectedOptions() as $optionData) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $optionData = \explode('/', base64_decode($optionData->getId())); + + if ($this->isProviderApplicable($optionData) === false) { + continue; + } + $this->validateInput($optionData); + + [, $linkId] = $optionData; + $linksData[] = $linkId; + } + + return ['links' => $linksData]; + } + + /** + * Checks whether this provider is applicable for the current option + * + * @param array $optionData + * @return bool + */ + private function isProviderApplicable(array $optionData): bool + { + if ($optionData[0] !== self::OPTION_TYPE) { + return false; + } + + return true; + } + + /** + * Validates the provided options structure + * + * @param array $optionData + * @throws LocalizedException + */ + private function validateInput(array $optionData): void + { + if (count($optionData) !== 2) { + throw new LocalizedException( + __('Wrong format of the entered option data. Must be "downloadable/link_id"') + ); + } + } +} diff --git a/app/code/Magento/QuoteDownloadableLinks/README.md b/app/code/Magento/QuoteDownloadableLinks/README.md new file mode 100644 index 0000000000000..68efffcea6fb8 --- /dev/null +++ b/app/code/Magento/QuoteDownloadableLinks/README.md @@ -0,0 +1,3 @@ +# QuoteDownloadableLinks + +**QuoteDownloadableLinks** provides data provider for creating buy request for links of downloadable products. diff --git a/app/code/Magento/QuoteDownloadableLinks/composer.json b/app/code/Magento/QuoteDownloadableLinks/composer.json new file mode 100644 index 0000000000000..dd840efb65c45 --- /dev/null +++ b/app/code/Magento/QuoteDownloadableLinks/composer.json @@ -0,0 +1,22 @@ +{ + "name": "magento/module-quote-downloadable-links", + "description": "N/A", + "type": "magento2-module", + "require": { + "php": "~7.1.3||~7.2.0||~7.3.0", + "magento/framework": "*", + "magento/module-quote": "*" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\QuoteDownloadableLinks\\": "" + } + } +} diff --git a/app/code/Magento/QuoteDownloadableLinks/etc/graphql/di.xml b/app/code/Magento/QuoteDownloadableLinks/etc/graphql/di.xml new file mode 100644 index 0000000000000..a932d199983a3 --- /dev/null +++ b/app/code/Magento/QuoteDownloadableLinks/etc/graphql/di.xml @@ -0,0 +1,16 @@ + + + + + + + Magento\QuoteDownloadableLinks\Model\Cart\BuyRequest\DownloadableLinkDataProvider + + + + diff --git a/app/code/Magento/QuoteDownloadableLinks/etc/module.xml b/app/code/Magento/QuoteDownloadableLinks/etc/module.xml new file mode 100644 index 0000000000000..a0cc652ab9188 --- /dev/null +++ b/app/code/Magento/QuoteDownloadableLinks/etc/module.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/code/Magento/QuoteDownloadableLinks/registration.php b/app/code/Magento/QuoteDownloadableLinks/registration.php new file mode 100644 index 0000000000000..8b766e7fde06c --- /dev/null +++ b/app/code/Magento/QuoteDownloadableLinks/registration.php @@ -0,0 +1,10 @@ +getCartForUser = $getCartForUser; + $this->addProductsToCartService = $addProductsToCart; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (empty($args['cart_id'])) { + throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); + } + if (empty($args['cart_items']) || !is_array($args['cart_items']) + ) { + throw new GraphQlInputException(__('Required parameter "cart_items" is missing')); + } + + $maskedCartId = $args['cart_id']; + $cartItemsData = $args['cart_items']; + $storeId = (int)$context->getExtensionAttributes()->getStore()->getId(); + + // Shopping Cart validation + $this->getCartForUser->execute($maskedCartId, $context->getUserId(), $storeId); + + $cartItems = []; + foreach ($cartItemsData as $cartItemData) { + $cartItems[] = (new CartItemFactory())->create($cartItemData); + } + + /** @var AddProductsToCartOutput $addProductsToCartOutput */ + $addProductsToCartOutput = $this->addProductsToCartService->execute($maskedCartId, $cartItems); + + return [ + 'cart' => [ + 'model' => $addProductsToCartOutput->getCart(), + ], + 'userInputErrors' => \array_map( + function (Error $error) { + return [ + 'code' => $error->getCode(), + 'message' => $error->getMessage(), + 'path' => $error->getCartItemPosition() + ]; + }, + $addProductsToCartOutput->getErrors() + ) + ]; + } +} diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 1ca00d5ef7bdc..dc0bf89abb9d8 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -22,6 +22,7 @@ type Mutation { setPaymentMethodAndPlaceOrder(input: SetPaymentMethodAndPlaceOrderInput): PlaceOrderOutput @deprecated(reason: "Should use setPaymentMethodOnCart and placeOrder mutations in single request.") @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetPaymentAndPlaceOrder") mergeCarts(source_cart_id: String!, destination_cart_id: String!): Cart! @doc(description:"Merges the source cart into the destination cart") @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\MergeCarts") placeOrder(input: PlaceOrderInput): PlaceOrderOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\PlaceOrder") + addProductsToCart(cart_id: String!, cart_items: [CartItemInput!]!): AddProductsToCartOutput @doc(description:"Add products to the cart. Provide single interface for all product types ") @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddProductsToCart") } input createEmptyCartInput { @@ -51,6 +52,9 @@ input VirtualProductCartItemInput { input CartItemInput { sku: String! quantity: Float! + parent_sku: String, + selected_options: [String!] + entered_options: [EnteredOptionInput!] } input CustomizableOptionInput { @@ -313,6 +317,11 @@ type SetGuestEmailOnCartOutput { cart: Cart! } +input EnteredOptionInput { + id: String! @doc(description: "base64 encoded option ID") + value: String! +} + type SimpleCartItem implements CartItemInterface @doc(description: "Simple Cart Item") { customizable_options: [SelectedCustomizableOption] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\CustomizableOptions") } @@ -366,3 +375,22 @@ type Order { order_number: String! order_id: String @deprecated(reason: "The order_id field is deprecated, use order_number instead.") } + +type CheckoutUserInputError @doc(description:"An error encountered while adding an item the the cart.") { + message: String! @doc(description: "Localized error message") + path: [String]! @doc(description: "Path to the input field that caused an error. See the GraphQL specification about path errors for details: http://spec.graphql.org/draft/#sec-Errors") + code: CheckoutUserInputErrorCodes! @doc(description: "Checkout-specific error code") +} + +type AddProductsToCartOutput { + cart: Cart! + userInputErrors:[CheckoutUserInputError]! +} + +enum CheckoutUserInputErrorCodes { + PRODUCT_NOT_FOUND + NOT_SALABLE + INSUFFICIENT_STOCK + UNDEFINED +} + diff --git a/app/code/Magento/SalesGraphQl/etc/schema.graphqls b/app/code/Magento/SalesGraphQl/etc/schema.graphqls index f823c25cf2d9f..4350c3889ef09 100644 --- a/app/code/Magento/SalesGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SalesGraphQl/etc/schema.graphqls @@ -27,16 +27,6 @@ type ReorderItemsOutput { userInputErrors:[CheckoutUserInputError]! @doc(description:"An array of reordering errors.") } -type CheckoutUserInputError @doc(description:"An error encountered while adding an item the the cart."){ - message: String! @doc(description: "Localized error message") - path: [String]! @doc(description: "Path to the input field that caused an error. See the GraphQL specification about path errors for details: http://spec.graphql.org/draft/#sec-Errors") - code: CheckoutUserInputErrorCodes! @doc(description: "Checkout-specific error code") -} - enum CheckoutUserInputErrorCodes { REORDER_NOT_AVAILABLE - PRODUCT_NOT_FOUND - NOT_SALABLE - INSUFFICIENT_STOCK - UNDEFINED } diff --git a/composer.json b/composer.json index 5223fa2a0aca4..0cd398c9999e2 100644 --- a/composer.json +++ b/composer.json @@ -216,6 +216,9 @@ "magento/module-product-video": "*", "magento/module-quote": "*", "magento/module-quote-analytics": "*", + "magento/module-quote-bundle-options": "*", + "magento/module-quote-configurable-options": "*", + "magento/module-quote-downloadable-links": "*", "magento/module-quote-graph-ql": "*", "magento/module-related-product-graph-ql": "*", "magento/module-release-notification": "*", diff --git a/composer.lock b/composer.lock index 37849c7c21a29..e9373ea2fb62e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1f11bed01d000a3d3eeda8b462e29f75", + "content-hash": "6863a98c354991b95809545739e59c8f", "packages": [ { "name": "braintree/braintree_php", @@ -201,16 +201,16 @@ }, { "name": "composer/ca-bundle", - "version": "1.2.6", + "version": "1.2.7", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "47fe531de31fca4a1b997f87308e7d7804348f7e" + "reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/47fe531de31fca4a1b997f87308e7d7804348f7e", - "reference": "47fe531de31fca4a1b997f87308e7d7804348f7e", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/95c63ab2117a72f48f5a55da9740a3273d45b7fd", + "reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd", "shasum": "" }, "require": { @@ -253,20 +253,30 @@ "ssl", "tls" ], - "time": "2020-01-13T10:02:55+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2020-04-08T08:27:21+00:00" }, { "name": "composer/composer", - "version": "1.10.1", + "version": "1.10.6", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "b912a45da3e2b22f5cb5a23e441b697a295ba011" + "reference": "be81b9c4735362c26876bdbfd3b5bc7e7f711c88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/b912a45da3e2b22f5cb5a23e441b697a295ba011", - "reference": "b912a45da3e2b22f5cb5a23e441b697a295ba011", + "url": "https://api.github.com/repos/composer/composer/zipball/be81b9c4735362c26876bdbfd3b5bc7e7f711c88", + "reference": "be81b9c4735362c26876bdbfd3b5bc7e7f711c88", "shasum": "" }, "require": { @@ -285,7 +295,8 @@ "symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0" }, "conflict": { - "symfony/console": "2.8.38" + "symfony/console": "2.8.38", + "symfony/phpunit-bridge": "3.4.40" }, "require-dev": { "phpspec/prophecy": "^1.10", @@ -333,7 +344,17 @@ "dependency", "package" ], - "time": "2020-03-13T19:34:27+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2020-05-06T08:28:10+00:00" }, { "name": "composer/semver", @@ -698,23 +719,24 @@ }, { "name": "guzzlehttp/guzzle", - "version": "6.5.2", + "version": "6.5.3", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "43ece0e75098b7ecd8d13918293029e555a50f82" + "reference": "aab4ebd862aa7d04f01a4b51849d657db56d882e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/43ece0e75098b7ecd8d13918293029e555a50f82", - "reference": "43ece0e75098b7ecd8d13918293029e555a50f82", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/aab4ebd862aa7d04f01a4b51849d657db56d882e", + "reference": "aab4ebd862aa7d04f01a4b51849d657db56d882e", "shasum": "" }, "require": { "ext-json": "*", "guzzlehttp/promises": "^1.0", "guzzlehttp/psr7": "^1.6.1", - "php": ">=5.5" + "php": ">=5.5", + "symfony/polyfill-intl-idn": "^1.11" }, "require-dev": { "ext-curl": "*", @@ -722,7 +744,6 @@ "psr/log": "^1.1" }, "suggest": { - "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, "type": "library", @@ -761,7 +782,7 @@ "rest", "web service" ], - "time": "2019-12-23T11:57:10+00:00" + "time": "2020-04-18T10:38:46+00:00" }, { "name": "guzzlehttp/promises", @@ -1243,16 +1264,16 @@ }, { "name": "laminas/laminas-db", - "version": "2.11.2", + "version": "2.11.3", "source": { "type": "git", "url": "https://github.com/laminas/laminas-db.git", - "reference": "76f9527da996c2fef32ef1f3a939e18ca5e9d962" + "reference": "6c4238918b9204db1eb8cafae2c1940d40f4c007" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-db/zipball/76f9527da996c2fef32ef1f3a939e18ca5e9d962", - "reference": "76f9527da996c2fef32ef1f3a939e18ca5e9d962", + "url": "https://api.github.com/repos/laminas/laminas-db/zipball/6c4238918b9204db1eb8cafae2c1940d40f4c007", + "reference": "6c4238918b9204db1eb8cafae2c1940d40f4c007", "shasum": "" }, "require": { @@ -1261,7 +1282,7 @@ "php": "^5.6 || ^7.0" }, "replace": { - "zendframework/zend-db": "self.version" + "zendframework/zend-db": "^2.11.0" }, "require-dev": { "laminas/laminas-coding-standard": "~1.0.0", @@ -1301,7 +1322,7 @@ "db", "laminas" ], - "time": "2020-01-14T13:07:26+00:00" + "time": "2020-03-29T12:08:51+00:00" }, { "name": "laminas/laminas-dependency-plugin", @@ -1402,16 +1423,16 @@ }, { "name": "laminas/laminas-diactoros", - "version": "1.8.7p1", + "version": "1.8.7p2", "source": { "type": "git", "url": "https://github.com/laminas/laminas-diactoros.git", - "reference": "56a9aca1f89231763d24d2ae13531b97fa5f4029" + "reference": "6991c1af7c8d2c8efee81b22ba97024781824aaa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/56a9aca1f89231763d24d2ae13531b97fa5f4029", - "reference": "56a9aca1f89231763d24d2ae13531b97fa5f4029", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/6991c1af7c8d2c8efee81b22ba97024781824aaa", + "reference": "6991c1af7c8d2c8efee81b22ba97024781824aaa", "shasum": "" }, "require": { @@ -1423,7 +1444,7 @@ "psr/http-message-implementation": "1.0" }, "replace": { - "zendframework/zend-diactoros": "self.version" + "zendframework/zend-diactoros": "~1.8.7.0" }, "require-dev": { "ext-dom": "*", @@ -1473,7 +1494,7 @@ "psr", "psr-7" ], - "time": "2020-01-07T19:25:17+00:00" + "time": "2020-03-23T15:28:28+00:00" }, { "name": "laminas/laminas-escaper", @@ -1584,16 +1605,16 @@ }, { "name": "laminas/laminas-feed", - "version": "2.12.1", + "version": "2.12.2", "source": { "type": "git", "url": "https://github.com/laminas/laminas-feed.git", - "reference": "c9356994eb80d0f6b46d7e12ba048d450bf0cd72" + "reference": "8a193ac96ebcb3e16b6ee754ac2a889eefacb654" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-feed/zipball/c9356994eb80d0f6b46d7e12ba048d450bf0cd72", - "reference": "c9356994eb80d0f6b46d7e12ba048d450bf0cd72", + "url": "https://api.github.com/repos/laminas/laminas-feed/zipball/8a193ac96ebcb3e16b6ee754ac2a889eefacb654", + "reference": "8a193ac96ebcb3e16b6ee754ac2a889eefacb654", "shasum": "" }, "require": { @@ -1605,7 +1626,7 @@ "php": "^5.6 || ^7.0" }, "replace": { - "zendframework/zend-feed": "self.version" + "zendframework/zend-feed": "^2.12.0" }, "require-dev": { "laminas/laminas-cache": "^2.7.2", @@ -1647,20 +1668,20 @@ "feed", "laminas" ], - "time": "2020-03-23T10:40:31+00:00" + "time": "2020-03-29T12:36:29+00:00" }, { "name": "laminas/laminas-filter", - "version": "2.9.3", + "version": "2.9.4", "source": { "type": "git", "url": "https://github.com/laminas/laminas-filter.git", - "reference": "52b5cdbef8902280996e687e7352a648a8e22f31" + "reference": "3c4476e772a062cef7531c6793377ae585d89c82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/52b5cdbef8902280996e687e7352a648a8e22f31", - "reference": "52b5cdbef8902280996e687e7352a648a8e22f31", + "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/3c4476e772a062cef7531c6793377ae585d89c82", + "reference": "3c4476e772a062cef7531c6793377ae585d89c82", "shasum": "" }, "require": { @@ -1672,7 +1693,7 @@ "laminas/laminas-validator": "<2.10.1" }, "replace": { - "zendframework/zend-filter": "self.version" + "zendframework/zend-filter": "^2.9.2" }, "require-dev": { "laminas/laminas-coding-standard": "~1.0.0", @@ -1716,20 +1737,20 @@ "filter", "laminas" ], - "time": "2020-01-07T20:43:53+00:00" + "time": "2020-03-29T12:41:29+00:00" }, { "name": "laminas/laminas-form", - "version": "2.14.4", + "version": "2.14.5", "source": { "type": "git", "url": "https://github.com/laminas/laminas-form.git", - "reference": "8b985f74bfe32910edb4ba9503877c4310228cd2" + "reference": "3e22e09751cf6ae031be87a44e092e7925ce5b7b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-form/zipball/8b985f74bfe32910edb4ba9503877c4310228cd2", - "reference": "8b985f74bfe32910edb4ba9503877c4310228cd2", + "url": "https://api.github.com/repos/laminas/laminas-form/zipball/3e22e09751cf6ae031be87a44e092e7925ce5b7b", + "reference": "3e22e09751cf6ae031be87a44e092e7925ce5b7b", "shasum": "" }, "require": { @@ -1740,7 +1761,7 @@ "php": "^5.6 || ^7.0" }, "replace": { - "zendframework/zend-form": "self.version" + "zendframework/zend-form": "^2.14.3" }, "require-dev": { "doctrine/annotations": "~1.0", @@ -1798,7 +1819,7 @@ "form", "laminas" ], - "time": "2020-03-18T22:38:54+00:00" + "time": "2020-03-29T12:46:16+00:00" }, { "name": "laminas/laminas-http", @@ -1924,16 +1945,16 @@ }, { "name": "laminas/laminas-i18n", - "version": "2.10.2", + "version": "2.10.3", "source": { "type": "git", "url": "https://github.com/laminas/laminas-i18n.git", - "reference": "9699c98d97d2f519def3da8d4128dfe6e6ad11bf" + "reference": "94ff957a1366f5be94f3d3a9b89b50386649e3ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/9699c98d97d2f519def3da8d4128dfe6e6ad11bf", - "reference": "9699c98d97d2f519def3da8d4128dfe6e6ad11bf", + "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/94ff957a1366f5be94f3d3a9b89b50386649e3ae", + "reference": "94ff957a1366f5be94f3d3a9b89b50386649e3ae", "shasum": "" }, "require": { @@ -1946,7 +1967,7 @@ "phpspec/prophecy": "<1.9.0" }, "replace": { - "zendframework/zend-i18n": "self.version" + "zendframework/zend-i18n": "^2.10.1" }, "require-dev": { "laminas/laminas-cache": "^2.6.1", @@ -1995,7 +2016,7 @@ "i18n", "laminas" ], - "time": "2020-03-20T11:57:14+00:00" + "time": "2020-03-29T12:51:08+00:00" }, { "name": "laminas/laminas-inputfilter", @@ -2242,16 +2263,16 @@ }, { "name": "laminas/laminas-mail", - "version": "2.10.0", + "version": "2.10.1", "source": { "type": "git", "url": "https://github.com/laminas/laminas-mail.git", - "reference": "019fb670c1dff6be7fc91d3b88942bd0a5f68792" + "reference": "cfe0711446c8d9c392e9fc664c9ccc180fa89005" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-mail/zipball/019fb670c1dff6be7fc91d3b88942bd0a5f68792", - "reference": "019fb670c1dff6be7fc91d3b88942bd0a5f68792", + "url": "https://api.github.com/repos/laminas/laminas-mail/zipball/cfe0711446c8d9c392e9fc664c9ccc180fa89005", + "reference": "cfe0711446c8d9c392e9fc664c9ccc180fa89005", "shasum": "" }, "require": { @@ -2265,7 +2286,7 @@ "true/punycode": "^2.1" }, "replace": { - "zendframework/zend-mail": "self.version" + "zendframework/zend-mail": "^2.10.0" }, "require-dev": { "laminas/laminas-coding-standard": "~1.0.0", @@ -2304,7 +2325,13 @@ "laminas", "mail" ], - "time": "2019-12-31T17:21:22+00:00" + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2020-04-21T16:42:19+00:00" }, { "name": "laminas/laminas-math", @@ -2362,16 +2389,16 @@ }, { "name": "laminas/laminas-mime", - "version": "2.7.3", + "version": "2.7.4", "source": { "type": "git", "url": "https://github.com/laminas/laminas-mime.git", - "reference": "e844abb02e868fae154207929190292ad25057cc" + "reference": "e45a7d856bf7b4a7b5bd00d6371f9961dc233add" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-mime/zipball/e844abb02e868fae154207929190292ad25057cc", - "reference": "e844abb02e868fae154207929190292ad25057cc", + "url": "https://api.github.com/repos/laminas/laminas-mime/zipball/e45a7d856bf7b4a7b5bd00d6371f9961dc233add", + "reference": "e45a7d856bf7b4a7b5bd00d6371f9961dc233add", "shasum": "" }, "require": { @@ -2380,7 +2407,7 @@ "php": "^5.6 || ^7.0" }, "replace": { - "zendframework/zend-mime": "self.version" + "zendframework/zend-mime": "^2.7.2" }, "require-dev": { "laminas/laminas-coding-standard": "~1.0.0", @@ -2412,7 +2439,7 @@ "laminas", "mime" ], - "time": "2020-03-06T08:38:03+00:00" + "time": "2020-03-29T13:12:07+00:00" }, { "name": "laminas/laminas-modulemanager", @@ -2797,16 +2824,16 @@ }, { "name": "laminas/laminas-session", - "version": "2.9.2", + "version": "2.9.3", "source": { "type": "git", "url": "https://github.com/laminas/laminas-session.git", - "reference": "fdba34c1b257235dba2fff6ed4df1844390f85f6" + "reference": "519e8966146536cd97c1cc3d59a21b095fb814d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-session/zipball/fdba34c1b257235dba2fff6ed4df1844390f85f6", - "reference": "fdba34c1b257235dba2fff6ed4df1844390f85f6", + "url": "https://api.github.com/repos/laminas/laminas-session/zipball/519e8966146536cd97c1cc3d59a21b095fb814d7", + "reference": "519e8966146536cd97c1cc3d59a21b095fb814d7", "shasum": "" }, "require": { @@ -2816,7 +2843,7 @@ "php": "^5.6 || ^7.0" }, "replace": { - "zendframework/zend-session": "self.version" + "zendframework/zend-session": "^2.9.1" }, "require-dev": { "container-interop/container-interop": "^1.1", @@ -2864,7 +2891,7 @@ "laminas", "session" ], - "time": "2020-03-06T09:44:45+00:00" + "time": "2020-03-29T13:26:04+00:00" }, { "name": "laminas/laminas-soap", @@ -3078,16 +3105,16 @@ }, { "name": "laminas/laminas-validator", - "version": "2.13.2", + "version": "2.13.4", "source": { "type": "git", "url": "https://github.com/laminas/laminas-validator.git", - "reference": "e7bf6a2eedc0508ebde0ebc66662efeb0abbcb2c" + "reference": "93593684e70b8ed1e870cacd34ca32b0c0ace185" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/e7bf6a2eedc0508ebde0ebc66662efeb0abbcb2c", - "reference": "e7bf6a2eedc0508ebde0ebc66662efeb0abbcb2c", + "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/93593684e70b8ed1e870cacd34ca32b0c0ace185", + "reference": "93593684e70b8ed1e870cacd34ca32b0c0ace185", "shasum": "" }, "require": { @@ -3097,7 +3124,7 @@ "php": "^7.1" }, "replace": { - "zendframework/zend-validator": "self.version" + "zendframework/zend-validator": "^2.13.0" }, "require-dev": { "laminas/laminas-cache": "^2.6.1", @@ -3153,7 +3180,7 @@ "laminas", "validator" ], - "time": "2020-03-16T11:38:27+00:00" + "time": "2020-03-31T18:57:01+00:00" }, { "name": "laminas/laminas-view", @@ -3248,16 +3275,16 @@ }, { "name": "laminas/laminas-zendframework-bridge", - "version": "1.0.2", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/laminas/laminas-zendframework-bridge.git", - "reference": "faf68f6109ceeff24241226033ab59640c7eb63b" + "reference": "bfbbdb6c998d50dbf69d2187cb78a5f1fa36e1e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/faf68f6109ceeff24241226033ab59640c7eb63b", - "reference": "faf68f6109ceeff24241226033ab59640c7eb63b", + "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/bfbbdb6c998d50dbf69d2187cb78a5f1fa36e1e9", + "reference": "bfbbdb6c998d50dbf69d2187cb78a5f1fa36e1e9", "shasum": "" }, "require": { @@ -3296,7 +3323,13 @@ "laminas", "zf" ], - "time": "2020-03-26T16:07:12+00:00" + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2020-04-03T16:01:00+00:00" }, { "name": "magento/composer", @@ -3863,16 +3896,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "2.0.26", + "version": "2.0.27", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "09655fcc1f8bab65727be036b28f6f20311c126c" + "reference": "34620af4df7d1988d8f0d7e91f6c8a3bf931d8dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/09655fcc1f8bab65727be036b28f6f20311c126c", - "reference": "09655fcc1f8bab65727be036b28f6f20311c126c", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/34620af4df7d1988d8f0d7e91f6c8a3bf931d8dc", + "reference": "34620af4df7d1988d8f0d7e91f6c8a3bf931d8dc", "shasum": "" }, "require": { @@ -3951,7 +3984,21 @@ "x.509", "x509" ], - "time": "2020-03-13T04:15:39+00:00" + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2020-04-04T23:17:33+00:00" }, { "name": "psr/container", @@ -4269,20 +4316,20 @@ }, { "name": "seld/jsonlint", - "version": "1.7.2", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19" + "reference": "ff2aa5420bfbc296cf6a0bc785fa5b35736de7c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e2e5d290e4d2a4f0eb449f510071392e00e10d19", - "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/ff2aa5420bfbc296cf6a0bc785fa5b35736de7c1", + "reference": "ff2aa5420bfbc296cf6a0bc785fa5b35736de7c1", "shasum": "" }, "require": { - "php": "^5.3 || ^7.0" + "php": "^5.3 || ^7.0 || ^8.0" }, "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" @@ -4314,7 +4361,17 @@ "parser", "validator" ], - "time": "2019-10-24T14:27:39+00:00" + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", + "type": "tidelift" + } + ], + "time": "2020-04-30T19:05:18+00:00" }, { "name": "seld/phar-utils", @@ -4362,16 +4419,16 @@ }, { "name": "symfony/console", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "4fa15ae7be74e53f6ec8c83ed403b97e23b665e9" + "reference": "10bb3ee3c97308869d53b3e3d03f6ac23ff985f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/4fa15ae7be74e53f6ec8c83ed403b97e23b665e9", - "reference": "4fa15ae7be74e53f6ec8c83ed403b97e23b665e9", + "url": "https://api.github.com/repos/symfony/console/zipball/10bb3ee3c97308869d53b3e3d03f6ac23ff985f7", + "reference": "10bb3ee3c97308869d53b3e3d03f6ac23ff985f7", "shasum": "" }, "require": { @@ -4434,20 +4491,34 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2020-02-24T13:10:00+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-30T11:41:10+00:00" }, { "name": "symfony/css-selector", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "d0a6dd288fa8848dcc3d1f58b94de6a7cc5d2d22" + "reference": "afc26133a6fbdd4f8842e38893e0ee4685c7c94b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/d0a6dd288fa8848dcc3d1f58b94de6a7cc5d2d22", - "reference": "d0a6dd288fa8848dcc3d1f58b94de6a7cc5d2d22", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/afc26133a6fbdd4f8842e38893e0ee4685c7c94b", + "reference": "afc26133a6fbdd4f8842e38893e0ee4685c7c94b", "shasum": "" }, "require": { @@ -4487,20 +4558,34 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2020-02-04T09:01:01+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:54:36+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "4ad8e149799d3128621a3a1f70e92b9897a8930d" + "reference": "abc8e3618bfdb55e44c8c6a00abd333f831bbfed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/4ad8e149799d3128621a3a1f70e92b9897a8930d", - "reference": "4ad8e149799d3128621a3a1f70e92b9897a8930d", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/abc8e3618bfdb55e44c8c6a00abd333f831bbfed", + "reference": "abc8e3618bfdb55e44c8c6a00abd333f831bbfed", "shasum": "" }, "require": { @@ -4557,7 +4642,21 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2020-02-04T09:32:40+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:54:36+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -4619,16 +4718,16 @@ }, { "name": "symfony/filesystem", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "266c9540b475f26122b61ef8b23dd9198f5d1cfd" + "reference": "a3ebf3bfd8a98a147c010a568add5a8aa4edea0f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/266c9540b475f26122b61ef8b23dd9198f5d1cfd", - "reference": "266c9540b475f26122b61ef8b23dd9198f5d1cfd", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/a3ebf3bfd8a98a147c010a568add5a8aa4edea0f", + "reference": "a3ebf3bfd8a98a147c010a568add5a8aa4edea0f", "shasum": "" }, "require": { @@ -4665,20 +4764,34 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2020-01-21T08:20:44+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-04-12T14:39:55+00:00" }, { "name": "symfony/finder", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "ea69c129aed9fdeca781d4b77eb20b62cf5d5357" + "reference": "5729f943f9854c5781984ed4907bbb817735776b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/ea69c129aed9fdeca781d4b77eb20b62cf5d5357", - "reference": "ea69c129aed9fdeca781d4b77eb20b62cf5d5357", + "url": "https://api.github.com/repos/symfony/finder/zipball/5729f943f9854c5781984ed4907bbb817735776b", + "reference": "5729f943f9854c5781984ed4907bbb817735776b", "shasum": "" }, "require": { @@ -4714,7 +4827,21 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2020-02-14T07:42:58+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:54:36+00:00" }, { "name": "symfony/polyfill-ctype", @@ -4774,6 +4901,68 @@ ], "time": "2020-02-27T09:26:54+00:00" }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf", + "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.15-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "time": "2020-03-09T19:04:49+00:00" + }, { "name": "symfony/polyfill-mbstring", "version": "v1.15.0", @@ -4833,6 +5022,61 @@ ], "time": "2020-03-09T19:04:49+00:00" }, + { + "name": "symfony/polyfill-php72", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "37b0976c78b94856543260ce09b460a7bc852747" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/37b0976c78b94856543260ce09b460a7bc852747", + "reference": "37b0976c78b94856543260ce09b460a7bc852747", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.15-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2020-02-27T09:26:54+00:00" + }, { "name": "symfony/polyfill-php73", "version": "v1.15.0", @@ -4893,16 +5137,16 @@ }, { "name": "symfony/process", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "bf9166bac906c9e69fb7a11d94875e7ced97bcd7" + "reference": "4b6a9a4013baa65d409153cbb5a895bf093dc7f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/bf9166bac906c9e69fb7a11d94875e7ced97bcd7", - "reference": "bf9166bac906c9e69fb7a11d94875e7ced97bcd7", + "url": "https://api.github.com/repos/symfony/process/zipball/4b6a9a4013baa65d409153cbb5a895bf093dc7f4", + "reference": "4b6a9a4013baa65d409153cbb5a895bf093dc7f4", "shasum": "" }, "require": { @@ -4938,7 +5182,21 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2020-02-07T20:06:44+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-04-15T15:56:18+00:00" }, { "name": "symfony/service-contracts", @@ -5414,16 +5672,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.133.45", + "version": "3.137.4", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "928a23e2ee7e195a66f93d0758895e26958c3b7d" + "reference": "0bed146e6336ca1260e937fce2f4931681ceb3ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/928a23e2ee7e195a66f93d0758895e26958c3b7d", - "reference": "928a23e2ee7e195a66f93d0758895e26958c3b7d", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/0bed146e6336ca1260e937fce2f4931681ceb3ce", + "reference": "0bed146e6336ca1260e937fce2f4931681ceb3ce", "shasum": "" }, "require": { @@ -5494,7 +5752,7 @@ "s3", "sdk" ], - "time": "2020-03-26T18:12:15+00:00" + "time": "2020-05-06T18:19:09+00:00" }, { "name": "behat/gherkin", @@ -5969,20 +6227,21 @@ }, { "name": "doctrine/annotations", - "version": "v1.8.0", + "version": "1.10.2", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "904dca4eb10715b92569fbcd79e201d5c349b6bc" + "reference": "b9d758e831c70751155c698c2f7df4665314a1cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/904dca4eb10715b92569fbcd79e201d5c349b6bc", - "reference": "904dca4eb10715b92569fbcd79e201d5c349b6bc", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/b9d758e831c70751155c698c2f7df4665314a1cb", + "reference": "b9d758e831c70751155c698c2f7df4665314a1cb", "shasum": "" }, "require": { "doctrine/lexer": "1.*", + "ext-tokenizer": "*", "php": "^7.1" }, "require-dev": { @@ -5992,7 +6251,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7.x-dev" + "dev-master": "1.9.x-dev" } }, "autoload": { @@ -6033,7 +6292,7 @@ "docblock", "parser" ], - "time": "2019-10-01T18:55:10+00:00" + "time": "2020-04-20T09:18:32+00:00" }, { "name": "doctrine/cache", @@ -6567,16 +6826,16 @@ }, { "name": "league/flysystem", - "version": "1.0.66", + "version": "1.0.67", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "021569195e15f8209b1c4bebb78bd66aa4f08c21" + "reference": "5b1f36c75c4bdde981294c2a0ebdb437ee6f275e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/021569195e15f8209b1c4bebb78bd66aa4f08c21", - "reference": "021569195e15f8209b1c4bebb78bd66aa4f08c21", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/5b1f36c75c4bdde981294c2a0ebdb437ee6f275e", + "reference": "5b1f36c75c4bdde981294c2a0ebdb437ee6f275e", "shasum": "" }, "require": { @@ -6647,7 +6906,13 @@ "sftp", "storage" ], - "time": "2020-03-17T18:58:12+00:00" + "funding": [ + { + "url": "https://offset.earth/frankdejonge", + "type": "other" + } + ], + "time": "2020-04-16T13:21:26+00:00" }, { "name": "lusitanian/oauth", @@ -7404,24 +7669,21 @@ }, { "name": "phpdocumentor/reflection-common", - "version": "2.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a" + "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a", - "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/6568f4687e5b41b054365f9ae03fcb1ed5f2069b", + "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b", "shasum": "" }, "require": { "php": ">=7.1" }, - "require-dev": { - "phpunit/phpunit": "~6" - }, "type": "library", "extra": { "branch-alias": { @@ -7452,7 +7714,7 @@ "reflection", "static analysis" ], - "time": "2018-08-07T13:53:10+00:00" + "time": "2020-04-27T09:25:28+00:00" }, { "name": "phpdocumentor/reflection-docblock", @@ -7741,21 +8003,24 @@ }, { "name": "phpstan/phpstan", - "version": "0.12.18", + "version": "0.12.23", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "1ce27fe29c8660a27926127d350d53d80c4d4286" + "reference": "71e529efced79e055fa8318b692e7f7d03ea4e75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1ce27fe29c8660a27926127d350d53d80c4d4286", - "reference": "1ce27fe29c8660a27926127d350d53d80c4d4286", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/71e529efced79e055fa8318b692e7f7d03ea4e75", + "reference": "71e529efced79e055fa8318b692e7f7d03ea4e75", "shasum": "" }, "require": { "php": "^7.1" }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, "bin": [ "phpstan", "phpstan.phar" @@ -7776,7 +8041,21 @@ "MIT" ], "description": "PHPStan - PHP Static Analysis Tool", - "time": "2020-03-22T16:51:47+00:00" + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpstan", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2020-05-05T12:55:44+00:00" }, { "name": "phpunit/php-code-coverage", @@ -8970,16 +9249,16 @@ }, { "name": "symfony/browser-kit", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "090ce406505149d6852a7c03b0346dec3b8cf612" + "reference": "e4b0dc1b100bf75b5717c5b451397f230a618a42" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/090ce406505149d6852a7c03b0346dec3b8cf612", - "reference": "090ce406505149d6852a7c03b0346dec3b8cf612", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/e4b0dc1b100bf75b5717c5b451397f230a618a42", + "reference": "e4b0dc1b100bf75b5717c5b451397f230a618a42", "shasum": "" }, "require": { @@ -9025,20 +9304,34 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2020-02-23T10:00:59+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-28T10:15:50+00:00" }, { "name": "symfony/config", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "cbfef5ae91ccd3b06621c18d58cd355c68c87ae9" + "reference": "8ba41fe053683e1e6e3f6fa21f07ea5c4dd9e4c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/cbfef5ae91ccd3b06621c18d58cd355c68c87ae9", - "reference": "cbfef5ae91ccd3b06621c18d58cd355c68c87ae9", + "url": "https://api.github.com/repos/symfony/config/zipball/8ba41fe053683e1e6e3f6fa21f07ea5c4dd9e4c0", + "reference": "8ba41fe053683e1e6e3f6fa21f07ea5c4dd9e4c0", "shasum": "" }, "require": { @@ -9089,20 +9382,34 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2020-02-04T09:32:40+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-04-15T15:56:18+00:00" }, { "name": "symfony/dependency-injection", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "ebb2e882e8c9e2eb990aa61ddcd389848466e342" + "reference": "9d0c2807962f7f12264ab459f48fb541dbd386bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/ebb2e882e8c9e2eb990aa61ddcd389848466e342", - "reference": "ebb2e882e8c9e2eb990aa61ddcd389848466e342", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/9d0c2807962f7f12264ab459f48fb541dbd386bd", + "reference": "9d0c2807962f7f12264ab459f48fb541dbd386bd", "shasum": "" }, "require": { @@ -9162,20 +9469,34 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2020-02-29T09:50:10+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-04-16T16:36:56+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "11dcf08f12f29981bf770f097a5d64d65bce5929" + "reference": "4d0fb3374324071ecdd94898367a3fa4b5563162" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/11dcf08f12f29981bf770f097a5d64d65bce5929", - "reference": "11dcf08f12f29981bf770f097a5d64d65bce5929", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/4d0fb3374324071ecdd94898367a3fa4b5563162", + "reference": "4d0fb3374324071ecdd94898367a3fa4b5563162", "shasum": "" }, "require": { @@ -9223,20 +9544,34 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2020-02-29T10:05:28+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-29T19:12:22+00:00" }, { "name": "symfony/http-foundation", - "version": "v5.0.5", + "version": "v5.0.8", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "6f9c2ba72f4295d7ce6cf9f79dbb18036291d335" + "reference": "e47fdf8b24edc12022ba52923150ec6484d7f57d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6f9c2ba72f4295d7ce6cf9f79dbb18036291d335", - "reference": "6f9c2ba72f4295d7ce6cf9f79dbb18036291d335", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e47fdf8b24edc12022ba52923150ec6484d7f57d", + "reference": "e47fdf8b24edc12022ba52923150ec6484d7f57d", "shasum": "" }, "require": { @@ -9278,20 +9613,34 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2020-02-14T07:43:07+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-04-18T20:50:06+00:00" }, { "name": "symfony/mime", - "version": "v5.0.5", + "version": "v5.0.8", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "9b3e5b5e58c56bbd76628c952d2b78556d305f3c" + "reference": "5d6c81c39225a750f3f43bee15f03093fb9aaa0b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/9b3e5b5e58c56bbd76628c952d2b78556d305f3c", - "reference": "9b3e5b5e58c56bbd76628c952d2b78556d305f3c", + "url": "https://api.github.com/repos/symfony/mime/zipball/5d6c81c39225a750f3f43bee15f03093fb9aaa0b", + "reference": "5d6c81c39225a750f3f43bee15f03093fb9aaa0b", "shasum": "" }, "require": { @@ -9340,20 +9689,34 @@ "mime", "mime-type" ], - "time": "2020-02-04T09:41:09+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-04-17T03:29:44+00:00" }, { "name": "symfony/options-resolver", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "9a02d6662660fe7bfadad63b5f0b0718d4c8b6b0" + "reference": "ade3d89dd3b875b83c8cff2980c9bb0daf6ef297" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/9a02d6662660fe7bfadad63b5f0b0718d4c8b6b0", - "reference": "9a02d6662660fe7bfadad63b5f0b0718d4c8b6b0", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/ade3d89dd3b875b83c8cff2980c9bb0daf6ef297", + "reference": "ade3d89dd3b875b83c8cff2980c9bb0daf6ef297", "shasum": "" }, "require": { @@ -9394,69 +9757,21 @@ "configuration", "options" ], - "time": "2020-01-04T13:00:46+00:00" - }, - { - "name": "symfony/polyfill-intl-idn", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf", - "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php72": "^1.10" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.15-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Intl\\Idn\\": "" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ { - "name": "Laurent Bassin", - "email": "laurent@bassin.info" + "url": "https://github.com/fabpot", + "type": "github" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "idn", - "intl", - "polyfill", - "portable", - "shim" - ], - "time": "2020-03-09T19:04:49+00:00" + "time": "2020-04-06T10:16:26+00:00" }, { "name": "symfony/polyfill-php70", @@ -9517,73 +9832,18 @@ ], "time": "2020-02-27T09:26:54+00:00" }, - { - "name": "symfony/polyfill-php72", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "37b0976c78b94856543260ce09b460a7bc852747" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/37b0976c78b94856543260ce09b460a7bc852747", - "reference": "37b0976c78b94856543260ce09b460a7bc852747", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.15-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "time": "2020-02-27T09:26:54+00:00" - }, { "name": "symfony/stopwatch", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "abc08d7c48987829bac301347faa10f7e8bbf4fb" + "reference": "e0324d3560e4128270e3f08617480d9233d81cfc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/abc08d7c48987829bac301347faa10f7e8bbf4fb", - "reference": "abc08d7c48987829bac301347faa10f7e8bbf4fb", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/e0324d3560e4128270e3f08617480d9233d81cfc", + "reference": "e0324d3560e4128270e3f08617480d9233d81cfc", "shasum": "" }, "require": { @@ -9620,20 +9880,34 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2020-01-04T13:00:46+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:54:36+00:00" }, { "name": "symfony/yaml", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "94d005c176db2080e98825d98e01e8b311a97a88" + "reference": "b385dce1c0e9f839b384af90188638819433e252" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/94d005c176db2080e98825d98e01e8b311a97a88", - "reference": "94d005c176db2080e98825d98e01e8b311a97a88", + "url": "https://api.github.com/repos/symfony/yaml/zipball/b385dce1c0e9f839b384af90188638819433e252", + "reference": "b385dce1c0e9f839b384af90188638819433e252", "shasum": "" }, "require": { @@ -9679,7 +9953,21 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2020-02-03T10:46:43+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-04-28T17:55:16+00:00" }, { "name": "theseer/fdomdocument", @@ -9763,25 +10051,31 @@ }, { "name": "vlucas/phpdotenv", - "version": "v2.6.1", + "version": "v2.6.4", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "2a7dcf7e3e02dc5e701004e51a6f304b713107d5" + "reference": "67d472b1794c986381a8950e4958e1adb779d561" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2a7dcf7e3e02dc5e701004e51a6f304b713107d5", - "reference": "2a7dcf7e3e02dc5e701004e51a6f304b713107d5", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/67d472b1794c986381a8950e4958e1adb779d561", + "reference": "67d472b1794c986381a8950e4958e1adb779d561", "shasum": "" }, "require": { - "php": ">=5.3.9", + "php": "^5.3.9 || ^7.0 || ^8.0", "symfony/polyfill-ctype": "^1.9" }, "require-dev": { + "ext-filter": "*", + "ext-pcre": "*", "phpunit/phpunit": "^4.8.35 || ^5.0" }, + "suggest": { + "ext-filter": "Required to use the boolean validator.", + "ext-pcre": "Required to use most of the library." + }, "type": "library", "extra": { "branch-alias": { @@ -9798,10 +10092,15 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "homepage": "https://gjcampbell.co.uk/" + }, { "name": "Vance Lucas", "email": "vance@vancelucas.com", - "homepage": "http://www.vancelucas.com" + "homepage": "https://vancelucas.com/" } ], "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", @@ -9810,20 +10109,30 @@ "env", "environment" ], - "time": "2019-01-29T11:11:52+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2020-05-02T13:38:00+00:00" }, { "name": "webmozart/assert", - "version": "1.7.0", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "aed98a490f9a8f78468232db345ab9cf606cf598" + "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/aed98a490f9a8f78468232db345ab9cf606cf598", - "reference": "aed98a490f9a8f78468232db345ab9cf606cf598", + "url": "https://api.github.com/repos/webmozart/assert/zipball/ab2cb0b3b559010b75981b1bdce728da3ee90ad6", + "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6", "shasum": "" }, "require": { @@ -9831,7 +10140,7 @@ "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "vimeo/psalm": "<3.6.0" + "vimeo/psalm": "<3.9.1" }, "require-dev": { "phpunit/phpunit": "^4.8.36 || ^7.5.13" @@ -9858,7 +10167,7 @@ "check", "validate" ], - "time": "2020-02-14T12:15:55+00:00" + "time": "2020-04-18T12:12:48+00:00" }, { "name": "weew/helpers-array", @@ -9926,5 +10235,6 @@ "ext-zip": "*", "lib-libxml": "*" }, - "platform-dev": [] + "platform-dev": [], + "plugin-api-version": "1.1.0" } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php new file mode 100644 index 0000000000000..4a7b181295d92 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php @@ -0,0 +1,225 @@ +quoteResource = $objectManager->get(QuoteResource::class); + $this->quote = $objectManager->create(Quote::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + $this->productRepository = $objectManager->get(ProductRepositoryInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Bundle/_files/product_1.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testAddBundleProductToCart() + { + $sku = 'bundle-product'; + + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + + $product = $this->productRepository->get($sku); + + /** @var $typeInstance \Magento\Bundle\Model\Product\Type */ + $typeInstance = $product->getTypeInstance(); + $typeInstance->setStoreFilter($product->getStoreId(), $product); + /** @var $option \Magento\Bundle\Model\Option */ + $option = $typeInstance->getOptionsCollection($product)->getFirstItem(); + /** @var \Magento\Catalog\Model\Product $selection */ + $selection = $typeInstance->getSelectionsCollection([$option->getId()], $product)->getFirstItem(); + $optionId = $option->getId(); + $selectionId = $selection->getSelectionId(); + + $bundleOptionIdV2 = $this->generateBundleOptionIdV2((int) $optionId, (int) $selectionId, 1); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = <<graphQlMutation($query); + + self::assertArrayHasKey('addProductsToCart', $response); + self::assertArrayHasKey('cart', $response['addProductsToCart']); + $cart = $response['addProductsToCart']['cart']; + $bundleItem = current($cart['items']); + self::assertEquals($sku, $bundleItem['product']['sku']); + $bundleItemOption = current($bundleItem['bundle_options']); + self::assertEquals($optionId, $bundleItemOption['id']); + self::assertEquals($option->getTitle(), $bundleItemOption['label']); + self::assertEquals($option->getType(), $bundleItemOption['type']); + $value = current($bundleItemOption['values']); + self::assertEquals($selection->getSelectionId(), $value['id']); + self::assertEquals((float) $selection->getSelectionPriceValue(), $value['price']); + self::assertEquals(1, $value['quantity']); + } + + /** + * @param int $optionId + * @param int $selectionId + * @param int $quantity + * @return string + */ + private function generateBundleOptionIdV2(int $optionId, int $selectionId, int $quantity): string + { + return base64_encode("bundle/$optionId/$selectionId/$quantity"); + } + + public function dataProviderTestUpdateBundleItemQuantity(): array + { + return [ + [2], + [0], + ]; + } + + /** + * @magentoApiDataFixture Magento/Bundle/_files/product_1.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + * @expectedExceptionMessage Please select all required options + */ + public function testAddBundleToCartWithWrongBundleOptions() + { + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + + $bundleOptionIdV2 = $this->generateBundleOptionIdV2((int) 1, (int) 1, 1); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = <<graphQlMutation($query); + + self::assertEquals( + "Please select all required options.", + $response['addProductsToCart']['userInputErrors'][0]['message'] + ); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php new file mode 100644 index 0000000000000..a56d55528b59b --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php @@ -0,0 +1,292 @@ +getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + } + + /** + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testAddConfigurableProductToCart() + { + $product = $this->getConfigurableProductInfo(); + $quantity = 2; + $parentSku = $product['sku']; + $attributeId = (int) $product['configurable_options'][0]['attribute_id']; + $valueIndex = $product['configurable_options'][0]['values'][1]['value_index']; + + $selectedConfigurableOptionsQuery = $this->generateSuperAttributesIdV2Query($attributeId, $valueIndex); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + + $query = $this->getQuery( + $maskedQuoteId, + $product['sku'], + 2, + $selectedConfigurableOptionsQuery + ); + + $response = $this->graphQlMutation($query); + + $cartItem = current($response['addProductsToCart']['cart']['items']); + self::assertEquals($quantity, $cartItem['quantity']); + self::assertEquals($parentSku, $cartItem['product']['sku']); + self::assertArrayHasKey('configurable_options', $cartItem); + + $option = current($cartItem['configurable_options']); + self::assertEquals($attributeId, $option['id']); + self::assertEquals($valueIndex, $option['value_id']); + self::assertArrayHasKey('option_label', $option); + self::assertArrayHasKey('value_label', $option); + } + + /** + * Generates Id_v2 for super configurable product super attributes + * + * @param int $attributeId + * @param int $valueIndex + * @return string + */ + private function generateSuperAttributesIdV2Query(int $attributeId, int $valueIndex): string + { + return 'selected_options: ["' . base64_encode("configurable/$attributeId/$valueIndex") . '"]'; + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/configurable_products_with_custom_attribute_layered_navigation.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testAddConfigurableProductWithWrongSuperAttributes() + { + $product = $this->getConfigurableProductInfo(); + $quantity = 2; + $parentSku = $product['sku']; + + $selectedConfigurableOptionsQuery = $this->generateSuperAttributesIdV2Query(0, 0); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + + $query = $this->getQuery( + $maskedQuoteId, + $parentSku, + $quantity, + $selectedConfigurableOptionsQuery + ); + + $response = $this->graphQlMutation($query); + + self::assertEquals( + 'You need to choose options for your item.', + $response['addProductsToCart']['userInputErrors'][0]['message'] + ); + } + + /** + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable_sku.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testAddProductIfQuantityIsNotAvailable() + { + $product = $this->getConfigurableProductInfo(); + $parentSku = $product['sku']; + $attributeId = (int) $product['configurable_options'][0]['attribute_id']; + $valueIndex = $product['configurable_options'][0]['values'][1]['value_index']; + + $selectedConfigurableOptionsQuery = $this->generateSuperAttributesIdV2Query($attributeId, $valueIndex); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + + $query = $this->getQuery( + $maskedQuoteId, + $parentSku, + 2000, + $selectedConfigurableOptionsQuery + ); + + $response = $this->graphQlMutation($query); + + self::assertEquals( + 'Could not add the product with SKU configurable to the shopping cart: The requested qty is not available', + $response['addProductsToCart']['userInputErrors'][0]['message'] + ); + } + + /** + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable_sku.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testAddNonExistentConfigurableProductParentToCart() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + $parentSku = 'configurable_no_exist'; + + $query = $this->getQuery( + $maskedQuoteId, + $parentSku, + 1, + '' + ); + + $response = $this->graphQlMutation($query); + + self::assertEquals( + 'Could not find a product with SKU "configurable_no_exist"', + $response['addProductsToCart']['userInputErrors'][0]['message'] + ); + } + + /** + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable_zero_qty_first_child.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testOutOfStockVariationToCart() + { + $product = $this->getConfigurableProductInfo(); + $attributeId = (int) $product['configurable_options'][0]['attribute_id']; + $valueIndex = $product['configurable_options'][0]['values'][0]['value_index']; + $parentSku = $product['sku']; + + $configurableOptionsQuery = $this->generateSuperAttributesIdV2Query($attributeId, $valueIndex); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + + $query = $this->getQuery( + $maskedQuoteId, + $parentSku, + 1, + $configurableOptionsQuery + ); + + $response = $this->graphQlMutation($query); + + $expectedErrorMessage = 'Could not add the product with SKU configurable to the shopping cart: ' . + 'There are no source items with the in stock status'; + self::assertEquals($expectedErrorMessage, $response['addProductsToCart']['userInputErrors'][0]['message']); + } + + /** + * @param string $maskedQuoteId + * @param string $parentSku + * @param int $quantity + * @param string $selectedOptionsQuery + * @return string + */ + private function getQuery( + string $maskedQuoteId, + string $parentSku, + int $quantity, + string $selectedOptionsQuery + ): string { + return <<graphQlQuery($this->getFetchProductQuery('configurable')); + return current($searchResponse['products']['items']); + } + + /** + * Returns GraphQl query for fetching configurable product information + * + * @param string $term + * @return string + */ + private function getFetchProductQuery(string $term): string + { + return <<objectManager = Bootstrap::getObjectManager(); + $this->getMaskedQuoteIdByReservedOrderId = $this->objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + $this->getCustomOptionsWithIDV2ForQueryBySku = + $this->objectManager->get(GetCustomOptionsWithIDV2ForQueryBySku::class); + } + + /** + * @magentoApiDataFixture Magento/Downloadable/_files/product_downloadable_with_custom_options.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testAddDownloadableProductWithOptions() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + + $sku = 'downloadable-product-with-purchased-separately-links'; + $qty = 1; + $links = $this->getProductsLinks($sku); + $linkId = key($links); + + $itemOptions = $this->getCustomOptionsWithIDV2ForQueryBySku->execute($sku); + $decodedItemOptions = $this->decodeCustomOptions($itemOptions); + + /* Add downloadable product link data to the "selected_options" */ + $itemOptions['selected_options'][] = $this->generateProductLinkSelectedOptions($linkId); + + $productOptionsQuery = preg_replace( + '/"([^"]+)"\s*:\s*/', + '$1:', + json_encode($itemOptions) + ); + + $query = $this->getQuery($maskedQuoteId, $qty, $sku, trim($productOptionsQuery, '{}')); + $response = $this->graphQlMutation($query); + + self::assertArrayHasKey('items', $response['addProductsToCart']['cart']); + self::assertCount($qty, $response['addProductsToCart']['cart']); + self::assertEquals($linkId, $response['addProductsToCart']['cart']['items'][0]['links'][0]['id']); + + $customizableOptionsOutput = + $response['addProductsToCart']['cart']['items'][0]['customizable_options']; + + foreach ($customizableOptionsOutput as $customizableOptionOutput) { + $customizableOptionOutputValues = []; + foreach ($customizableOptionOutput['values'] as $customizableOptionOutputValue) { + $customizableOptionOutputValues[] = $customizableOptionOutputValue['value']; + } + if (count($customizableOptionOutputValues) === 1) { + $customizableOptionOutputValues = $customizableOptionOutputValues[0]; + } + + self::assertEquals( + $decodedItemOptions[$customizableOptionOutput['id']], + $customizableOptionOutputValues + ); + } + } + + /** + * Function returns array of all product's links + * + * @param string $sku + * @return array + */ + private function getProductsLinks(string $sku) : array + { + $result = []; + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + + $product = $productRepository->get($sku, false, null, true); + + foreach ($product->getDownloadableLinks() as $linkObject) { + $result[$linkObject->getLinkId()] = [ + 'title' => $linkObject->getTitle(), + 'link_type' => null, //deprecated field + 'price' => $linkObject->getPrice(), + ]; + } + + return $result; + } + + /** + * Generates Id_v2 for downloadable links + * + * @param int $linkId + * @return string + */ + private function generateProductLinkSelectedOptions(int $linkId): string + { + return base64_encode("downloadable/$linkId"); + } + + /** + * Decodes ID_v2 for customizable options and returns [ID] = value pairs + * + * @param array $encodedCustomOptions + * @return array + */ + private function decodeCustomOptions(array $encodedCustomOptions): array + { + $customOptions = []; + + foreach ($encodedCustomOptions['selected_options'] as $selectedOption) { + [,$optionId, $optionValueId] = explode('/', base64_decode($selectedOption)); + if (isset($customOptions[$optionId])) { + $customOptions[$optionId] = [$customOptions[$optionId], $optionValueId]; + } else { + $customOptions[$optionId] = $optionValueId; + } + + } + + foreach ($encodedCustomOptions['entered_options'] as $enteredOption) { + [,$optionId] = explode('/', base64_decode($enteredOption['id'])); + $customOptions[$optionId] = $enteredOption['value']; + } + + return $customOptions; + } + + /** + * Returns GraphQl query string + * + * @param string $maskedQuoteId + * @param int $qty + * @param string $sku + * @param string $customizableOptions + * @return string + */ + private function getQuery( + string $maskedQuoteId, + int $qty, + string $sku, + string $customizableOptions + ): string { + return <<productCustomOptionRepository = $productCustomOptionRepository; + } + + /** + * Returns array of custom options for the product + * + * @param string $sku + * @return array + */ + public function execute(string $sku): array + { + $customOptions = $this->productCustomOptionRepository->getList($sku); + $selectedOptions = []; + $enteredOptions = []; + + foreach ($customOptions as $customOption) { + $optionType = $customOption->getType(); + + if ($optionType === 'field' || $optionType === 'area' || $optionType === 'date') { + $enteredOptions[] = [ + 'id' => $this->encodeEnteredOption((int) $customOption->getOptionId()), + 'value' => '2012-12-12' + ]; + } elseif ($optionType === 'drop_down') { + $optionSelectValues = $customOption->getValues(); + $selectedOptions[] = $this->encodeSelectedOption( + (int) $customOption->getOptionId(), + (int) reset($optionSelectValues)->getOptionTypeId() + ); + } elseif ($optionType === 'multiple') { + foreach ($customOption->getValues() as $optionValue) { + $selectedOptions[] = $this->encodeSelectedOption( + (int) $customOption->getOptionId(), + (int) $optionValue->getOptionTypeId() + ); + } + } + } + + return [ + 'selected_options' => $selectedOptions, + 'entered_options' => $enteredOptions + ]; + } + + /** + * Returns id_v2 of the selected custom option + * + * @param int $optionId + * @param int $optionValueId + * @return string + */ + private function encodeSelectedOption(int $optionId, int $optionValueId): string + { + return base64_encode("custom-option/$optionId/$optionValueId"); + } + + /** + * Returns id_v2 of the entered custom option + * + * @param int $optionId + * @return string + */ + private function encodeEnteredOption(int $optionId): string + { + return base64_encode("custom-option/$optionId"); + } +} diff --git a/vendor/.htaccess b/vendor/.htaccess deleted file mode 100644 index b97408bad3f2e..0000000000000 --- a/vendor/.htaccess +++ /dev/null @@ -1,7 +0,0 @@ - - order allow,deny - deny from all - -= 2.4> - Require all denied - From 13d09423fc882bb8dd6207f1645d1756322b82d6 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Wed, 13 May 2020 15:59:27 +0200 Subject: [PATCH 02/37] Review issues fixes --- app/code/Magento/Quote/Model/Cart/AddProductsToCart.php | 2 ++ .../Model/Cart/BuyRequest/CustomizableOptionDataProvider.php | 2 +- .../Magento/Quote/Model/Cart/Data/AddProductsToCartOutput.php | 2 +- app/code/Magento/Quote/Model/Cart/Data/EnteredOption.php | 3 +++ .../Model/Cart/BuyRequest/BundleDataProvider.php | 3 +-- .../Model/Cart/BuyRequest/SuperAttributeDataProvider.php | 2 +- .../Model/Cart/BuyRequest/DownloadableLinkDataProvider.php | 2 +- 7 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php index 5d7eb053dc892..79a11e9eba039 100644 --- a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php @@ -175,6 +175,8 @@ private function addError(string $message, int $cartItemPosition = 0, string $co /** * Get message error code. * + * TODO: introduce a separate class for getting error code from a message + * * @param string $message * @return string */ diff --git a/app/code/Magento/Quote/Model/Cart/BuyRequest/CustomizableOptionDataProvider.php b/app/code/Magento/Quote/Model/Cart/BuyRequest/CustomizableOptionDataProvider.php index 07fc9a41855a5..173f0bb403f62 100644 --- a/app/code/Magento/Quote/Model/Cart/BuyRequest/CustomizableOptionDataProvider.php +++ b/app/code/Magento/Quote/Model/Cart/BuyRequest/CustomizableOptionDataProvider.php @@ -96,7 +96,7 @@ private function validateInput(array $optionData): void { if (count($optionData) !== 3) { throw new LocalizedException( - __('Wrong format of the entered option data. Must be "custom-option/option_id/option_value_id"') + __('Wrong format of the entered option data') ); } } diff --git a/app/code/Magento/Quote/Model/Cart/Data/AddProductsToCartOutput.php b/app/code/Magento/Quote/Model/Cart/Data/AddProductsToCartOutput.php index 6088b12a36e7c..c12c02c0449f6 100644 --- a/app/code/Magento/Quote/Model/Cart/Data/AddProductsToCartOutput.php +++ b/app/code/Magento/Quote/Model/Cart/Data/AddProductsToCartOutput.php @@ -45,7 +45,7 @@ public function getCart(): CartInterface } /** - * Get errors happened during reorder + * Get errors happened during adding item to the cart * * @return Error[] */ diff --git a/app/code/Magento/Quote/Model/Cart/Data/EnteredOption.php b/app/code/Magento/Quote/Model/Cart/Data/EnteredOption.php index f11aa49d6b4c3..b60c349a8e99a 100644 --- a/app/code/Magento/Quote/Model/Cart/Data/EnteredOption.php +++ b/app/code/Magento/Quote/Model/Cart/Data/EnteredOption.php @@ -7,6 +7,9 @@ namespace Magento\Quote\Model\Cart\Data; +/** + * DTO for quote item entered option + */ class EnteredOption { /** diff --git a/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php b/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php index 946e30ce3e276..6e8c9844b1503 100644 --- a/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php +++ b/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php @@ -68,8 +68,7 @@ private function isProviderApplicable(array $optionData): bool private function validateInput(array $optionData): void { if (count($optionData) !== 4) { - $errorMessage = __('Wrong format of the entered option data. ' . - 'Must be "bundle/option_id/option_value_id/option_quantity"'); + $errorMessage = __('Wrong format of the entered option data'); throw new LocalizedException($errorMessage); } } diff --git a/app/code/Magento/QuoteConfigurableOptions/Model/Cart/BuyRequest/SuperAttributeDataProvider.php b/app/code/Magento/QuoteConfigurableOptions/Model/Cart/BuyRequest/SuperAttributeDataProvider.php index 13c822d7233e2..e7d5820be8a59 100644 --- a/app/code/Magento/QuoteConfigurableOptions/Model/Cart/BuyRequest/SuperAttributeDataProvider.php +++ b/app/code/Magento/QuoteConfigurableOptions/Model/Cart/BuyRequest/SuperAttributeDataProvider.php @@ -67,7 +67,7 @@ private function validateInput(array $optionData): void { if (count($optionData) !== 3) { throw new LocalizedException( - __('Wrong format of the entered option data. Must be "configurable/attribute_id/value_index"') + __('Wrong format of the entered option data') ); } } diff --git a/app/code/Magento/QuoteDownloadableLinks/Model/Cart/BuyRequest/DownloadableLinkDataProvider.php b/app/code/Magento/QuoteDownloadableLinks/Model/Cart/BuyRequest/DownloadableLinkDataProvider.php index 85bb62179eeb2..cf381a19e2ddd 100644 --- a/app/code/Magento/QuoteDownloadableLinks/Model/Cart/BuyRequest/DownloadableLinkDataProvider.php +++ b/app/code/Magento/QuoteDownloadableLinks/Model/Cart/BuyRequest/DownloadableLinkDataProvider.php @@ -68,7 +68,7 @@ private function validateInput(array $optionData): void { if (count($optionData) !== 2) { throw new LocalizedException( - __('Wrong format of the entered option data. Must be "downloadable/link_id"') + __('Wrong format of the entered option data') ); } } From aa5bd2bebb24274f7a7e3dd8fa514a983b4dc1f6 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Wed, 13 May 2020 16:06:56 +0200 Subject: [PATCH 03/37] Updated composer.lock --- composer.lock | 541 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 476 insertions(+), 65 deletions(-) diff --git a/composer.lock b/composer.lock index 350a2f9c5a2ed..d4bd427a36d61 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "087f8432a6f317056b40a0b8a160a2cf", + "content-hash": "186bf474b3518bdf285be837602257ca", "packages": [ { "name": "braintree/braintree_php", @@ -253,6 +253,16 @@ "ssl", "tls" ], + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], "time": "2020-04-08T08:27:21+00:00" }, { @@ -334,6 +344,16 @@ "dependency", "package" ], + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], "time": "2020-05-06T08:28:10+00:00" }, { @@ -2309,6 +2329,12 @@ "laminas", "mail" ], + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], "time": "2020-04-21T16:42:19+00:00" }, { @@ -3301,6 +3327,12 @@ "laminas", "zf" ], + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], "time": "2020-04-03T16:01:00+00:00" }, { @@ -3956,6 +3988,20 @@ "x.509", "x509" ], + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], "time": "2020-04-04T23:17:33+00:00" }, { @@ -4228,23 +4274,23 @@ }, { "name": "react/promise", - "version": "v2.7.1", + "version": "v2.8.0", "source": { "type": "git", "url": "https://github.com/reactphp/promise.git", - "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d" + "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/31ffa96f8d2ed0341a57848cbb84d88b89dd664d", - "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d", + "url": "https://api.github.com/repos/reactphp/promise/zipball/f3cff96a19736714524ca0dd1d4130de73dbbbc4", + "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4", "shasum": "" }, "require": { "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "~4.8" + "phpunit/phpunit": "^7.0 || ^6.5 || ^5.7 || ^4.8.36" }, "type": "library", "autoload": { @@ -4270,7 +4316,7 @@ "promise", "promises" ], - "time": "2019-01-07T21:25:54+00:00" + "time": "2020-05-12T15:16:56+00:00" }, { "name": "seld/jsonlint", @@ -4319,6 +4365,16 @@ "parser", "validator" ], + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", + "type": "tidelift" + } + ], "time": "2020-04-30T19:05:18+00:00" }, { @@ -4439,6 +4495,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-03-30T11:41:10+00:00" }, { @@ -4492,6 +4562,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-03-27T16:56:45+00:00" }, { @@ -4562,6 +4646,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-03-27T16:54:36+00:00" }, { @@ -4670,6 +4768,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-04-12T14:40:17+00:00" }, { @@ -4719,20 +4831,34 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-03-27T16:56:45+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.15.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14" + "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14", - "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e94c8b1bbe2bc77507a1056cdb06451c75b427f9", + "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9", "shasum": "" }, "require": { @@ -4744,7 +4870,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.17-dev" } }, "autoload": { @@ -4777,20 +4903,34 @@ "polyfill", "portable" ], - "time": "2020-02-27T09:26:54+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-05-12T16:14:59+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.15.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf" + "reference": "3bff59ea7047e925be6b7f2059d60af31bb46d6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf", - "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/3bff59ea7047e925be6b7f2059d60af31bb46d6a", + "reference": "3bff59ea7047e925be6b7f2059d60af31bb46d6a", "shasum": "" }, "require": { @@ -4804,7 +4944,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.17-dev" } }, "autoload": { @@ -4839,20 +4979,34 @@ "portable", "shim" ], - "time": "2020-03-09T19:04:49+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-05-12T16:47:27+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.15.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac" + "reference": "fa79b11539418b02fc5e1897267673ba2c19419c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac", - "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fa79b11539418b02fc5e1897267673ba2c19419c", + "reference": "fa79b11539418b02fc5e1897267673ba2c19419c", "shasum": "" }, "require": { @@ -4864,7 +5018,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.17-dev" } }, "autoload": { @@ -4898,20 +5052,34 @@ "portable", "shim" ], - "time": "2020-03-09T19:04:49+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-05-12T16:47:27+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.15.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "37b0976c78b94856543260ce09b460a7bc852747" + "reference": "f048e612a3905f34931127360bdd2def19a5e582" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/37b0976c78b94856543260ce09b460a7bc852747", - "reference": "37b0976c78b94856543260ce09b460a7bc852747", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/f048e612a3905f34931127360bdd2def19a5e582", + "reference": "f048e612a3905f34931127360bdd2def19a5e582", "shasum": "" }, "require": { @@ -4920,7 +5088,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.17-dev" } }, "autoload": { @@ -4953,20 +5121,34 @@ "portable", "shim" ], - "time": "2020-02-27T09:26:54+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-05-12T16:47:27+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.15.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7" + "reference": "a760d8964ff79ab9bf057613a5808284ec852ccc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7", - "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a760d8964ff79ab9bf057613a5808284ec852ccc", + "reference": "a760d8964ff79ab9bf057613a5808284ec852ccc", "shasum": "" }, "require": { @@ -4975,7 +5157,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.17-dev" } }, "autoload": { @@ -5011,7 +5193,21 @@ "portable", "shim" ], - "time": "2020-02-27T09:26:54+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-05-12T16:47:27+00:00" }, { "name": "symfony/process", @@ -5060,6 +5256,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-04-15T15:56:18+00:00" }, { @@ -5536,16 +5746,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.137.5", + "version": "3.137.8", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "b3309075ec2a2c695c33d8e21c07b3d5c10cdb9a" + "reference": "0116efff39048d9bc163d3c287a85cce30b9aa20" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/b3309075ec2a2c695c33d8e21c07b3d5c10cdb9a", - "reference": "b3309075ec2a2c695c33d8e21c07b3d5c10cdb9a", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/0116efff39048d9bc163d3c287a85cce30b9aa20", + "reference": "0116efff39048d9bc163d3c287a85cce30b9aa20", "shasum": "" }, "require": { @@ -5616,7 +5826,7 @@ "s3", "sdk" ], - "time": "2020-05-07T18:16:43+00:00" + "time": "2020-05-12T18:14:22+00:00" }, { "name": "behat/gherkin", @@ -5853,6 +6063,12 @@ "functional testing", "unit testing" ], + "funding": [ + { + "url": "https://opencollective.com/codeception", + "type": "open_collective" + } + ], "time": "2020-03-23T17:07:20+00:00" }, { @@ -6701,6 +6917,12 @@ } ], "description": "A tool to automatically fix PHP code style", + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], "time": "2020-04-15T18:51:10+00:00" }, { @@ -6879,16 +7101,16 @@ }, { "name": "league/flysystem", - "version": "1.0.67", + "version": "1.0.68", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "5b1f36c75c4bdde981294c2a0ebdb437ee6f275e" + "reference": "3e4198372276ec99ac3409a21d7c9d1ced9026e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/5b1f36c75c4bdde981294c2a0ebdb437ee6f275e", - "reference": "5b1f36c75c4bdde981294c2a0ebdb437ee6f275e", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/3e4198372276ec99ac3409a21d7c9d1ced9026e4", + "reference": "3e4198372276ec99ac3409a21d7c9d1ced9026e4", "shasum": "" }, "require": { @@ -6959,7 +7181,13 @@ "sftp", "storage" ], - "time": "2020-04-16T13:21:26+00:00" + "funding": [ + { + "url": "https://offset.earth/frankdejonge", + "type": "other" + } + ], + "time": "2020-05-12T20:33:44+00:00" }, { "name": "lusitanian/oauth", @@ -8060,16 +8288,16 @@ }, { "name": "phpstan/phpstan", - "version": "0.12.23", + "version": "0.12.25", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "71e529efced79e055fa8318b692e7f7d03ea4e75" + "reference": "9619551d68b2d4c0d681a8df73f3c847c798ee64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/71e529efced79e055fa8318b692e7f7d03ea4e75", - "reference": "71e529efced79e055fa8318b692e7f7d03ea4e75", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9619551d68b2d4c0d681a8df73f3c847c798ee64", + "reference": "9619551d68b2d4c0d681a8df73f3c847c798ee64", "shasum": "" }, "require": { @@ -8098,7 +8326,21 @@ "MIT" ], "description": "PHPStan - PHP Static Analysis Tool", - "time": "2020-05-05T12:55:44+00:00" + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpstan", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2020-05-10T20:36:16+00:00" }, { "name": "phpunit/php-code-coverage", @@ -8212,6 +8454,12 @@ "filesystem", "iterator" ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], "time": "2020-04-18T05:02:12+00:00" }, { @@ -8360,6 +8608,12 @@ "keywords": [ "timer" ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], "time": "2020-04-20T06:00:37+00:00" }, { @@ -8409,6 +8663,12 @@ "keywords": [ "tokenizer" ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], "time": "2020-05-06T09:56:31+00:00" }, { @@ -8497,6 +8757,16 @@ "testing", "xunit" ], + "funding": [ + { + "url": "https://phpunit.de/donate.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], "time": "2020-04-30T06:32:53+00:00" }, { @@ -8637,6 +8907,12 @@ ], "description": "Collection of value objects that represent the PHP code units", "homepage": "https://github.com/sebastianbergmann/code-unit", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], "time": "2020-04-30T05:58:10+00:00" }, { @@ -8750,16 +9026,16 @@ }, { "name": "sebastian/diff", - "version": "4.0.0", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "c0c26c9188b538bfa985ae10c9f05d278f12060d" + "reference": "3e523c576f29dacecff309f35e4cc5a5c168e78a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c0c26c9188b538bfa985ae10c9f05d278f12060d", - "reference": "c0c26c9188b538bfa985ae10c9f05d278f12060d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3e523c576f29dacecff309f35e4cc5a5c168e78a", + "reference": "3e523c576f29dacecff309f35e4cc5a5c168e78a", "shasum": "" }, "require": { @@ -8767,7 +9043,7 @@ }, "require-dev": { "phpunit/phpunit": "^9.0", - "symfony/process": "^4 || ^5" + "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { @@ -8802,7 +9078,13 @@ "unidiff", "unified diff" ], - "time": "2020-02-07T06:09:38+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-05-08T05:01:12+00:00" }, { "name": "sebastian/environment", @@ -8855,6 +9137,12 @@ "environment", "hhvm" ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], "time": "2020-04-14T13:36:52+00:00" }, { @@ -9467,6 +9755,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-04-15T15:59:10+00:00" }, { @@ -9540,6 +9842,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-04-28T17:58:55+00:00" }, { @@ -9595,6 +9911,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-04-18T20:50:06+00:00" }, { @@ -9657,6 +9987,20 @@ "mime", "mime-type" ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-04-17T03:29:44+00:00" }, { @@ -9711,20 +10055,34 @@ "configuration", "options" ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-04-06T10:40:56+00:00" }, { "name": "symfony/polyfill-php70", - "version": "v1.15.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "2a18e37a489803559284416df58c71ccebe50bf0" + "reference": "82225c2d7d23d7e70515496d249c0152679b468e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/2a18e37a489803559284416df58c71ccebe50bf0", - "reference": "2a18e37a489803559284416df58c71ccebe50bf0", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/82225c2d7d23d7e70515496d249c0152679b468e", + "reference": "82225c2d7d23d7e70515496d249c0152679b468e", "shasum": "" }, "require": { @@ -9734,7 +10092,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.17-dev" } }, "autoload": { @@ -9770,7 +10128,21 @@ "portable", "shim" ], - "time": "2020-02-27T09:26:54+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-05-12T16:47:27+00:00" }, { "name": "symfony/stopwatch", @@ -9820,6 +10192,20 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-03-27T16:56:45+00:00" }, { @@ -9879,6 +10265,20 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-04-28T17:58:55+00:00" }, { @@ -10021,6 +10421,16 @@ "env", "environment" ], + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], "time": "2020-05-02T13:38:00+00:00" }, { @@ -10136,5 +10546,6 @@ "ext-zip": "*", "lib-libxml": "*" }, - "platform-dev": [] + "platform-dev": [], + "plugin-api-version": "1.1.0" } From 3da33f5c242be2ee9bc56390769e2e9c6949511e Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Sat, 16 May 2020 11:00:42 +0200 Subject: [PATCH 04/37] Updated composer.json files of the modules --- app/code/Magento/QuoteBundleOptions/composer.json | 6 +++--- app/code/Magento/QuoteConfigurableOptions/composer.json | 6 +++--- app/code/Magento/QuoteDownloadableLinks/composer.json | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/QuoteBundleOptions/composer.json b/app/code/Magento/QuoteBundleOptions/composer.json index a26502c5a5d6c..a2651272018a8 100644 --- a/app/code/Magento/QuoteBundleOptions/composer.json +++ b/app/code/Magento/QuoteBundleOptions/composer.json @@ -1,12 +1,12 @@ { "name": "magento/module-quote-bundle-options", - "description": "N/A", - "type": "magento2-module", + "description": "Magento module provides data provider for creating buy request for bundle products", "require": { - "php": "~7.1.3||~7.2.0||~7.3.0", + "php": "~7.3.0||~7.4.0", "magento/framework": "*", "magento/module-quote": "*" }, + "type": "magento2-module", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/QuoteConfigurableOptions/composer.json b/app/code/Magento/QuoteConfigurableOptions/composer.json index 1221068c3061e..51d6933d5c6d6 100644 --- a/app/code/Magento/QuoteConfigurableOptions/composer.json +++ b/app/code/Magento/QuoteConfigurableOptions/composer.json @@ -1,12 +1,12 @@ { "name": "magento/module-quote-configurable-options", - "description": "N/A", - "type": "magento2-module", + "description": "Magento module provides data provider for creating buy request for configurable products", "require": { - "php": "~7.1.3||~7.2.0||~7.3.0", + "php": "~7.3.0||~7.4.0", "magento/framework": "*", "magento/module-quote": "*" }, + "type": "magento2-module", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/QuoteDownloadableLinks/composer.json b/app/code/Magento/QuoteDownloadableLinks/composer.json index dd840efb65c45..ad120dea96263 100644 --- a/app/code/Magento/QuoteDownloadableLinks/composer.json +++ b/app/code/Magento/QuoteDownloadableLinks/composer.json @@ -1,12 +1,12 @@ { "name": "magento/module-quote-downloadable-links", - "description": "N/A", - "type": "magento2-module", + "description": "Magento module provides data provider for creating buy request for links of downloadable products", "require": { - "php": "~7.1.3||~7.2.0||~7.3.0", + "php": "~7.3.0||~7.4.0", "magento/framework": "*", "magento/module-quote": "*" }, + "type": "magento2-module", "license": [ "OSL-3.0", "AFL-3.0" From 87407faf05799285b2ac80e8c3e70362a0e708ce Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Tue, 19 May 2020 17:45:09 +0200 Subject: [PATCH 05/37] Added PHPDoc description for the class --- app/code/Magento/Quote/Model/Cart/Data/SelectedOption.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Quote/Model/Cart/Data/SelectedOption.php b/app/code/Magento/Quote/Model/Cart/Data/SelectedOption.php index 8a15fb3e0dcb4..70edd93cd8ef8 100644 --- a/app/code/Magento/Quote/Model/Cart/Data/SelectedOption.php +++ b/app/code/Magento/Quote/Model/Cart/Data/SelectedOption.php @@ -7,6 +7,9 @@ namespace Magento\Quote\Model\Cart\Data; +/** + * DTO for quote item selected option + */ class SelectedOption { /** From 75f531d02c9f098ea7762a67aa4e4976e262f7fb Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Mon, 1 Jun 2020 19:49:05 +0200 Subject: [PATCH 06/37] Unnecessary interface has been removed --- .../Quote/Model/Cart/AddProductsToCart.php | 12 ++++++-- .../Model/Cart/AddProductsToCartInterface.php | 28 ------------------- 2 files changed, 9 insertions(+), 31 deletions(-) delete mode 100644 app/code/Magento/Quote/Model/Cart/AddProductsToCartInterface.php diff --git a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php index 79a11e9eba039..4e1cf53aeec69 100644 --- a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php @@ -18,9 +18,10 @@ use Magento\Framework\Message\MessageInterface; /** - * @inheritdoc + * Unified approach to add products to the Shopping Cart. + * Client code must validate, that customer is eligible to call service with provided {cartId} and {cartItems} */ -class AddProductsToCart implements AddProductsToCartInterface +class AddProductsToCart { /**#@+ * Error message codes @@ -88,7 +89,12 @@ public function __construct( } /** - * @inheritdoc + * Add cart items to the cart + * + * @param string $maskedCartId + * @param Data\CartItem[] $cartItems + * @return AddProductsToCartOutput + * @throws NoSuchEntityException Could not find a Cart with provided $maskedCartId */ public function execute(string $maskedCartId, array $cartItems): AddProductsToCartOutput { diff --git a/app/code/Magento/Quote/Model/Cart/AddProductsToCartInterface.php b/app/code/Magento/Quote/Model/Cart/AddProductsToCartInterface.php deleted file mode 100644 index 98e69af2a0179..0000000000000 --- a/app/code/Magento/Quote/Model/Cart/AddProductsToCartInterface.php +++ /dev/null @@ -1,28 +0,0 @@ - Date: Wed, 10 Jun 2020 11:19:12 +0200 Subject: [PATCH 07/37] Use CamelCase for mutation parameters --- .../Model/Resolver/AddProductsToCart.php | 12 ++++++------ app/code/Magento/QuoteGraphQl/etc/schema.graphqls | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/AddProductsToCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/AddProductsToCart.php index ccb6897e358d0..b8213cab61bba 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/AddProductsToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/AddProductsToCart.php @@ -51,16 +51,16 @@ public function __construct( */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { - if (empty($args['cart_id'])) { - throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); + if (empty($args['cartId'])) { + throw new GraphQlInputException(__('Required parameter "cartId" is missing')); } - if (empty($args['cart_items']) || !is_array($args['cart_items']) + if (empty($args['cartItems']) || !is_array($args['cartItems']) ) { - throw new GraphQlInputException(__('Required parameter "cart_items" is missing')); + throw new GraphQlInputException(__('Required parameter "cartItems" is missing')); } - $maskedCartId = $args['cart_id']; - $cartItemsData = $args['cart_items']; + $maskedCartId = $args['cartId']; + $cartItemsData = $args['cartItems']; $storeId = (int)$context->getExtensionAttributes()->getStore()->getId(); // Shopping Cart validation diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index dc0bf89abb9d8..725c34870a042 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -22,7 +22,7 @@ type Mutation { setPaymentMethodAndPlaceOrder(input: SetPaymentMethodAndPlaceOrderInput): PlaceOrderOutput @deprecated(reason: "Should use setPaymentMethodOnCart and placeOrder mutations in single request.") @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetPaymentAndPlaceOrder") mergeCarts(source_cart_id: String!, destination_cart_id: String!): Cart! @doc(description:"Merges the source cart into the destination cart") @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\MergeCarts") placeOrder(input: PlaceOrderInput): PlaceOrderOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\PlaceOrder") - addProductsToCart(cart_id: String!, cart_items: [CartItemInput!]!): AddProductsToCartOutput @doc(description:"Add products to the cart. Provide single interface for all product types ") @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddProductsToCart") + addProductsToCart(cartId: String!, cartItems: [CartItemInput!]!): AddProductsToCartOutput @doc(description:"Add products to the cart. Provide single interface for all product types ") @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddProductsToCart") } input createEmptyCartInput { From 4c08148d81850a0a717bf2625ea286664766b423 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Mon, 6 Jul 2020 15:19:48 +0200 Subject: [PATCH 08/37] Resolving merge conflicts --- app/code/Magento/SalesGraphQl/etc/schema.graphqls | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/code/Magento/SalesGraphQl/etc/schema.graphqls b/app/code/Magento/SalesGraphQl/etc/schema.graphqls index 6724e6cbdf7c3..243757d1cb8e2 100644 --- a/app/code/Magento/SalesGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SalesGraphQl/etc/schema.graphqls @@ -204,16 +204,6 @@ type KeyValue @doc(description: "The key-value type") { value: String @doc(description: "The value part of the name/value pair") } -type CheckoutUserInputError @doc(description:"An error encountered while adding an item the the cart.") { - message: String! @doc(description: "Localized error message") - path: [String]! @doc(description: "Path to the input field that caused an error. See the GraphQL specification about path errors for details: http://spec.graphql.org/draft/#sec-Errors") - code: CheckoutUserInputErrorCodes! @doc(description: "Checkout-specific error code") -} - enum CheckoutUserInputErrorCodes { REORDER_NOT_AVAILABLE - PRODUCT_NOT_FOUND - NOT_SALABLE - INSUFFICIENT_STOCK - UNDEFINED } From f5f6b5957488d87bac8e8ffa08c60b84ea294338 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Sun, 19 Jul 2020 14:59:19 +0200 Subject: [PATCH 09/37] Fixed error handling issues --- .../Quote/Model/Cart/AddProductsToCart.php | 20 ++++++++++--------- .../Model/Resolver/AddProductsToCart.php | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php index 4e1cf53aeec69..18d27a29b8ae1 100644 --- a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php @@ -36,6 +36,7 @@ class AddProductsToCart * List of error messages and codes. */ private const MESSAGE_CODES = [ + 'Could not find a product with SKU' => self::ERROR_PRODUCT_NOT_FOUND, 'The required options you selected are not available' => self::ERROR_NOT_SALABLE, 'Product that you are trying to add is not available' => self::ERROR_NOT_SALABLE, 'This product is out of stock' => self::ERROR_NOT_SALABLE, @@ -128,13 +129,18 @@ private function addItemToCart(CartInterface $cart, Data\CartItem $cartItem, int { $sku = $cartItem->getSku(); + if ($cartItem->getQuantity() <= 0) { + $this->addError(__('The product quantity should be greater than 0')->render()); + + return; + } + try { $product = $this->productRepository->get($sku, false, null, true); } catch (NoSuchEntityException $e) { $this->addError( __('Could not find a product with SKU "%sku"', ['sku' => $sku])->render(), - $cartItemPosition, - self::ERROR_PRODUCT_NOT_FOUND + $cartItemPosition ); return; @@ -144,10 +150,7 @@ private function addItemToCart(CartInterface $cart, Data\CartItem $cartItem, int $result = $cart->addProduct($product, $this->requestBuilder->build($cartItem)); } catch (\Throwable $e) { $this->addError( - __( - 'Could not add the product with SKU %sku to the shopping cart: %message', - ['sku' => $sku, 'message' => $e->getMessage()] - )->render(), + __($e->getMessage())->render(), $cartItemPosition ); return; @@ -166,14 +169,13 @@ private function addItemToCart(CartInterface $cart, Data\CartItem $cartItem, int * * @param string $message * @param int $cartItemPosition - * @param string|null $code * @return void */ - private function addError(string $message, int $cartItemPosition = 0, string $code = ''): void + private function addError(string $message, int $cartItemPosition = 0): void { $this->errors[] = new Data\Error( $message, - $code ?? $this->getErrorCode($message), + $this->getErrorCode($message), $cartItemPosition ); } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/AddProductsToCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/AddProductsToCart.php index b8213cab61bba..de89f3c062e95 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/AddProductsToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/AddProductsToCart.php @@ -83,7 +83,7 @@ function (Error $error) { return [ 'code' => $error->getCode(), 'message' => $error->getMessage(), - 'path' => $error->getCartItemPosition() + 'path' => [$error->getCartItemPosition()] ]; }, $addProductsToCartOutput->getErrors() From 52fb7a7aa911ad491a9e8c23f979e8582e31cbb8 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Mon, 20 Jul 2020 17:59:08 +0200 Subject: [PATCH 10/37] Error handling performance improvements --- .../Quote/Model/Cart/AddProductsToCart.php | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php index 18d27a29b8ae1..a9bc48994b380 100644 --- a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php @@ -38,7 +38,7 @@ class AddProductsToCart private const MESSAGE_CODES = [ 'Could not find a product with SKU' => self::ERROR_PRODUCT_NOT_FOUND, 'The required options you selected are not available' => self::ERROR_NOT_SALABLE, - 'Product that you are trying to add is not available' => self::ERROR_NOT_SALABLE, + 'Product that you are trying to add is not available.' => self::ERROR_NOT_SALABLE, 'This product is out of stock' => self::ERROR_NOT_SALABLE, 'There are no source items' => self::ERROR_NOT_SALABLE, 'The fewest you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, @@ -102,8 +102,8 @@ public function execute(string $maskedCartId, array $cartItems): AddProductsToCa $cartId = $this->maskedQuoteIdToQuoteId->execute($maskedCartId); $cart = $this->cartRepository->get($cartId); - foreach ($cartItems as $n => $cartItem) { - $this->addItemToCart($cart, $cartItem, $n); + foreach ($cartItems as $cartItemPosition => $cartItem) { + $this->addItemToCart($cart, $cartItem, $cartItemPosition); } if ($cart->getData('has_error')) { $errors = $cart->getErrors(); @@ -190,21 +190,14 @@ private function addError(string $message, int $cartItemPosition = 0): void */ private function getErrorCode(string $message): string { - $code = self::ERROR_UNDEFINED; - - $matchedCodes = array_filter( - self::MESSAGE_CODES, - function ($key) use ($message) { - return false !== strpos($message, $key); - }, - ARRAY_FILTER_USE_KEY - ); - - if (!empty($matchedCodes)) { - $code = current($matchedCodes); + foreach (self::MESSAGE_CODES as $codeMessage => $code) { + if (false !== stripos($codeMessage, $message)) { + return $code; + } } - return $code; + /* If no code was matched, return the default one */ + return self::ERROR_UNDEFINED; } /** From 85fd2700d238e7228a7f33787022af657d8b3817 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Tue, 21 Jul 2020 16:38:39 +0200 Subject: [PATCH 11/37] Fixing the case when an error message appears twice --- app/code/Magento/Quote/Model/Cart/AddProductsToCart.php | 4 +++- app/code/Magento/QuoteGraphQl/etc/schema.graphqls | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php index a9bc48994b380..26df021e6325b 100644 --- a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php @@ -39,7 +39,7 @@ class AddProductsToCart 'Could not find a product with SKU' => self::ERROR_PRODUCT_NOT_FOUND, 'The required options you selected are not available' => self::ERROR_NOT_SALABLE, 'Product that you are trying to add is not available.' => self::ERROR_NOT_SALABLE, - 'This product is out of stock' => self::ERROR_NOT_SALABLE, + 'This product is out of stock' => self::ERROR_INSUFFICIENT_STOCK, 'There are no source items' => self::ERROR_NOT_SALABLE, 'The fewest you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, 'The most you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, @@ -153,6 +153,8 @@ private function addItemToCart(CartInterface $cart, Data\CartItem $cartItem, int __($e->getMessage())->render(), $cartItemPosition ); + $cart->setHasError(false); + return; } diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 636f1ed1e4002..7dae2987125e9 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -320,7 +320,7 @@ type SetGuestEmailOnCartOutput { } input EnteredOptionInput { - id: String! @doc(description: "base64 encoded option ID") + id: ID! @doc(description: "base64 encoded option ID") value: String! } From 01e63121e9d9b32a3b89dfe30ef7c648a03e8cbb Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Sat, 25 Jul 2020 09:51:01 +0200 Subject: [PATCH 12/37] Fixing existing API-functional tests --- .../AddBundleProductToCartSingleMutationTest.php | 10 +++++----- ...AddConfigurableProductToCartSingleMutationTest.php | 11 +++++------ ...AddDownloadableProductToCartSingleMutationTest.php | 6 +++--- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php index 4a7b181295d92..33f2174134b3a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php @@ -42,7 +42,7 @@ class AddBundleProductToCartSingleMutationTest extends GraphQlAbstract /** * @inheritdoc */ - protected function setUp() + protected function setUp(): void { $objectManager = Bootstrap::getObjectManager(); $this->quoteResource = $objectManager->get(QuoteResource::class); @@ -83,8 +83,8 @@ public function testAddBundleProductToCart() $query = <<getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); @@ -131,7 +131,7 @@ public function testAddProductIfQuantityIsNotAvailable() $response = $this->graphQlMutation($query); self::assertEquals( - 'Could not add the product with SKU configurable to the shopping cart: The requested qty is not available', + 'The requested qty is not available', $response['addProductsToCart']['userInputErrors'][0]['message'] ); } @@ -183,8 +183,7 @@ public function testOutOfStockVariationToCart() $response = $this->graphQlMutation($query); - $expectedErrorMessage = 'Could not add the product with SKU configurable to the shopping cart: ' . - 'There are no source items with the in stock status'; + $expectedErrorMessage = 'This product is out of stock.'; self::assertEquals($expectedErrorMessage, $response['addProductsToCart']['userInputErrors'][0]['message']); } @@ -204,8 +203,8 @@ private function getQuery( return <<objectManager = Bootstrap::getObjectManager(); $this->getMaskedQuoteIdByReservedOrderId = $this->objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); @@ -175,8 +175,8 @@ private function getQuery( return << Date: Sat, 25 Jul 2020 14:15:21 +0200 Subject: [PATCH 13/37] Tests enhancements. Added test case for simple product with customizable options --- ...oadableProductToCartSingleMutationTest.php | 47 ++---- ...dSimpleProductToCartSingleMutationTest.php | 148 ++++++++++++++++++ .../Quote/GetCartItemOptionsFromUID.php | 46 ++++++ ... GetCustomOptionsWithUIDForQueryBySku.php} | 45 ++++-- 4 files changed, 239 insertions(+), 47 deletions(-) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartItemOptionsFromUID.php rename dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/{GetCustomOptionsWithIDV2ForQueryBySku.php => GetCustomOptionsWithUIDForQueryBySku.php} (60%) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddDownloadableProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddDownloadableProductToCartSingleMutationTest.php index f13a5ebfe42fa..b457b49a97807 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddDownloadableProductToCartSingleMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddDownloadableProductToCartSingleMutationTest.php @@ -28,10 +28,15 @@ class AddDownloadableProductToCartSingleMutationTest extends GraphQlAbstract private $objectManager; /** - * @var GetCustomOptionsWithIDV2ForQueryBySku + * @var GetCustomOptionsWithUIDForQueryBySku */ private $getCustomOptionsWithIDV2ForQueryBySku; + /** + * @var GetCartItemOptionsFromUID + */ + private $getCartItemOptionsFromUID; + /** * @inheritdoc */ @@ -39,8 +44,9 @@ protected function setUp(): void { $this->objectManager = Bootstrap::getObjectManager(); $this->getMaskedQuoteIdByReservedOrderId = $this->objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + $this->getCartItemOptionsFromUID = $this->objectManager->get(GetCartItemOptionsFromUID::class); $this->getCustomOptionsWithIDV2ForQueryBySku = - $this->objectManager->get(GetCustomOptionsWithIDV2ForQueryBySku::class); + $this->objectManager->get(GetCustomOptionsWithUIDForQueryBySku::class); } /** @@ -57,7 +63,14 @@ public function testAddDownloadableProductWithOptions() $linkId = key($links); $itemOptions = $this->getCustomOptionsWithIDV2ForQueryBySku->execute($sku); - $decodedItemOptions = $this->decodeCustomOptions($itemOptions); + $decodedItemOptions = $this->getCartItemOptionsFromUID->execute($itemOptions); + + /* The type field is only required for assertions, it should not be present in query */ + foreach ($itemOptions['entered_options'] as &$enteredOption) { + if (isset($enteredOption['type'])) { + unset($enteredOption['type']); + } + } /* Add downloadable product link data to the "selected_options" */ $itemOptions['selected_options'][] = $this->generateProductLinkSelectedOptions($linkId); @@ -129,34 +142,6 @@ private function generateProductLinkSelectedOptions(int $linkId): string return base64_encode("downloadable/$linkId"); } - /** - * Decodes ID_v2 for customizable options and returns [ID] = value pairs - * - * @param array $encodedCustomOptions - * @return array - */ - private function decodeCustomOptions(array $encodedCustomOptions): array - { - $customOptions = []; - - foreach ($encodedCustomOptions['selected_options'] as $selectedOption) { - [,$optionId, $optionValueId] = explode('/', base64_decode($selectedOption)); - if (isset($customOptions[$optionId])) { - $customOptions[$optionId] = [$customOptions[$optionId], $optionValueId]; - } else { - $customOptions[$optionId] = $optionValueId; - } - - } - - foreach ($encodedCustomOptions['entered_options'] as $enteredOption) { - [,$optionId] = explode('/', base64_decode($enteredOption['id'])); - $customOptions[$optionId] = $enteredOption['value']; - } - - return $customOptions; - } - /** * Returns GraphQl query string * diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php new file mode 100644 index 0000000000000..64f19a886a388 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php @@ -0,0 +1,148 @@ +getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + $this->getCartItemOptionsFromUID = $objectManager->get(GetCartItemOptionsFromUID::class); + $this->getCustomOptionsWithIDV2ForQueryBySku = $objectManager->get( + GetCustomOptionsWithUIDForQueryBySku::class + ); + } + + /** + * Test adding a simple product to the shopping cart with all supported + * customizable options assigned + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple_with_options.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testAddSimpleProductWithOptions() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + + $sku = 'simple'; + $qty = 1; + + $itemOptions = $this->getCustomOptionsWithIDV2ForQueryBySku->execute($sku); + $decodedItemOptions = $this->getCartItemOptionsFromUID->execute($itemOptions); + + /* The type field is only required for assertions, it should not be present in query */ + foreach ($itemOptions['entered_options'] as &$enteredOption) { + if (isset($enteredOption['type'])) { + unset($enteredOption['type']); + } + } + + $productOptionsQuery = preg_replace( + '/"([^"]+)"\s*:\s*/', + '$1:', + json_encode($itemOptions) + ); + + $query = $this->getQuery($maskedQuoteId, $qty, $sku, trim($productOptionsQuery, '{}')); + $response = $this->graphQlMutation($query); + + self::assertArrayHasKey('items', $response['addProductsToCart']['cart']); + self::assertCount($qty, $response['addProductsToCart']['cart']); + $customizableOptionsOutput = + $response['addProductsToCart']['cart']['items'][0]['customizable_options']; + + foreach ($customizableOptionsOutput as $customizableOptionOutput) { + $customizableOptionOutputValues = []; + foreach ($customizableOptionOutput['values'] as $customizableOptionOutputValue) { + $customizableOptionOutputValues[] = $customizableOptionOutputValue['value']; + } + if (count($customizableOptionOutputValues) === 1) { + $customizableOptionOutputValues = $customizableOptionOutputValues[0]; + } + + self::assertEquals( + $decodedItemOptions[$customizableOptionOutput['id']], + $customizableOptionOutputValues + ); + } + } + + /** + * Returns GraphQl query string + * + * @param string $maskedQuoteId + * @param int $qty + * @param string $sku + * @param string $customizableOptions + * @return string + */ + private function getQuery( + string $maskedQuoteId, + int $qty, + string $sku, + string $customizableOptions + ): string { + return <<getType(); - if ($optionType === 'field' || $optionType === 'area' || $optionType === 'date') { - $enteredOptions[] = [ - 'id' => $this->encodeEnteredOption((int) $customOption->getOptionId()), - 'value' => '2012-12-12' - ]; - } elseif ($optionType === 'drop_down') { - $optionSelectValues = $customOption->getValues(); - $selectedOptions[] = $this->encodeSelectedOption( - (int) $customOption->getOptionId(), - (int) reset($optionSelectValues)->getOptionTypeId() - ); - } elseif ($optionType === 'multiple') { - foreach ($customOption->getValues() as $optionValue) { + switch ($optionType) { + case 'field': + case 'area': + $enteredOptions[] = [ + 'type' => 'field', + 'id' => $this->encodeEnteredOption((int) $customOption->getOptionId()), + 'value' => 'test' + ]; + break; + case 'date': + $enteredOptions[] = [ + 'type' => 'date', + 'id' => $this->encodeEnteredOption((int) $customOption->getOptionId()), + 'value' => '2012-12-12 00:00:00' + ]; + break; + case 'drop_down': + $optionSelectValues = $customOption->getValues(); $selectedOptions[] = $this->encodeSelectedOption( (int) $customOption->getOptionId(), - (int) $optionValue->getOptionTypeId() + (int) reset($optionSelectValues)->getOptionTypeId() ); - } + break; + case 'multiple': + foreach ($customOption->getValues() as $optionValue) { + $selectedOptions[] = $this->encodeSelectedOption( + (int) $customOption->getOptionId(), + (int) $optionValue->getOptionTypeId() + ); + } + break; } } From 1521ac2a56ffe6d7a97d2c3c716619dde4873473 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Sun, 26 Jul 2020 13:32:42 +0200 Subject: [PATCH 14/37] API-functional test for adding product with wrong SKU --- ...dSimpleProductToCartSingleMutationTest.php | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php index 64f19a886a388..1315dcfa73ff6 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php @@ -97,6 +97,26 @@ public function testAddSimpleProductWithOptions() } } + /** + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + * @group wip + */ + public function testAddProductWithWrongSku() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + $sku = 'non-existent'; + + $query = $this->getQuery($maskedQuoteId, 1, $sku, ''); + $response = $this->graphQlMutation($query); + + self::assertArrayHasKey('userInputErrors', $response['addProductsToCart']); + self::assertCount(1, $response['addProductsToCart']['userInputErrors']); + self::assertEquals( + 'Could not find a product with SKU "' . $sku .'"', + $response['addProductsToCart']['userInputErrors'][0]['message'] + ); + } + /** * Returns GraphQl query string * From 9be928c3dd9bdd8ea6ffa413062ed98644e07857 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Sun, 26 Jul 2020 13:48:39 +0200 Subject: [PATCH 15/37] API-functional tests for wrong SKU and wrong quantity --- ...dSimpleProductToCartSingleMutationTest.php | 70 +++++++++++++++++-- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php index 1315dcfa73ff6..2ab0de101a8f0 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php @@ -98,13 +98,16 @@ public function testAddSimpleProductWithOptions() } /** + * @param string $sku + * @param string $message + * + * @dataProvider wrongSkuDataProvider + * * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php - * @group wip */ - public function testAddProductWithWrongSku() + public function testAddProductWithWrongSku(string $sku, string $message) { $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); - $sku = 'non-existent'; $query = $this->getQuery($maskedQuoteId, 1, $sku, ''); $response = $this->graphQlMutation($query); @@ -112,11 +115,70 @@ public function testAddProductWithWrongSku() self::assertArrayHasKey('userInputErrors', $response['addProductsToCart']); self::assertCount(1, $response['addProductsToCart']['userInputErrors']); self::assertEquals( - 'Could not find a product with SKU "' . $sku .'"', + $message, + $response['addProductsToCart']['userInputErrors'][0]['message'] + ); + } + + /** + * @param int $quantity + * @param string $message + * + * @dataProvider wrongQuantityDataProvider + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple_without_custom_options.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testAddProductWithWrongQuantity(int $quantity, string $message) + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + $sku = 'simple-2'; + + $query = $this->getQuery($maskedQuoteId, $quantity, $sku, ''); + $response = $this->graphQlMutation($query); + self::assertArrayHasKey('userInputErrors', $response['addProductsToCart']); + self::assertCount(1, $response['addProductsToCart']['userInputErrors']); + + self::assertEquals( + $message, $response['addProductsToCart']['userInputErrors'][0]['message'] ); } + /** + * @return array + */ + public function wrongSkuDataProvider(): array + { + return [ + 'Non-existent SKU' => [ + 'non-existent', + 'Could not find a product with SKU "non-existent"' + ], + 'Empty SKU' => [ + '', + 'Could not find a product with SKU ""' + ] + ]; + } + + /** + * @return array + */ + public function wrongQuantityDataProvider(): array + { + return [ + 'More quantity than in stock' => [ + 101, + 'The requested qty is not available' + ], + 'Quantity equals zero' => [ + 0, + 'The product quantity should be greater than 0' + ] + ]; + } + /** * Returns GraphQl query string * From bf83c7fcae3d046a03db9bc7f12759034bf810b5 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Wed, 29 Jul 2020 11:42:55 +0200 Subject: [PATCH 16/37] Added end-to-end test for adding product with customizable options to the shopping cart --- .../AddSimpleProductToCartEndToEndTest.php | 262 ++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartEndToEndTest.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartEndToEndTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartEndToEndTest.php new file mode 100644 index 0000000000000..a03c2eb1ff698 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartEndToEndTest.php @@ -0,0 +1,262 @@ +getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + $this->getCartItemOptionsFromUID = $objectManager->get(GetCartItemOptionsFromUID::class); + $this->getCustomOptionsWithIDV2ForQueryBySku = $objectManager->get( + GetCustomOptionsWithUIDForQueryBySku::class + ); + } + + /** + * Test adding a simple product to the shopping cart with all supported + * customizable options assigned + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple_with_options.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testAddSimpleProductWithOptions() + { + $sku = 'simple'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + $qty = 1; + + $productOptionsData = $this->getProductOptionsViaQuery($sku); + + $itemOptionsQuery = preg_replace( + '/"([^"]+)"\s*:\s*/', + '$1:', + json_encode($productOptionsData['received_options']) + ); + + $query = $this->getAddToCartMutation($maskedQuoteId, $qty, $sku, trim($itemOptionsQuery, '{}')); + $response = $this->graphQlMutation($query); + + self::assertArrayHasKey('customizable_options', $response['addProductsToCart']['cart']['items'][0]); + + foreach ($response['addProductsToCart']['cart']['items'][0]['customizable_options'] as $option) { + self::assertEquals($productOptionsData['expected_options'][$option['id']], $option['values'][0]['value']); + } + } + + /** + * Get product data with customizable options using GraphQl query + * + * @param string $sku + * @return array + * @throws \Exception + */ + private function getProductOptionsViaQuery(string $sku): array + { + $query = $this->getProductQuery($sku); + $response = $this->graphQlQuery($query); + self::assertArrayHasKey('options', $response['products']['items'][0]); + + $expectedItemOptions = []; + $receivedItemOptions = [ + 'entered_options' => [], + 'selected_options' => [] + ]; + + foreach ($response['products']['items'][0]['options'] as $option) { + if (isset($option['entered_option'])) { + /* The date normalization is required since the attribute might value is formatted by the system */ + if ($option['title'] === 'date option') { + $value = '2012-12-12 00:00:00'; + $expectedItemOptions[$option['option_id']] = date('M d, Y', strtotime($value)); + } else { + $value = 'test'; + $expectedItemOptions[$option['option_id']] = $value; + } + $value = $option['title'] === 'date option' ? '2012-12-12 00:00:00' : 'test'; + + $receivedItemOptions['entered_options'][] = [ + 'id' => $option['entered_option']['uid'], + 'value' => $value + ]; + + + } elseif (isset($option['selected_option'])) { + $receivedItemOptions['selected_options'][] = reset($option['selected_option'])['uid']; + $expectedItemOptions[$option['option_id']] = reset($option['selected_option'])['option_type_id']; + } + } + + return [ + 'expected_options' => $expectedItemOptions, + 'received_options' => $receivedItemOptions + ]; + } + + /** + * Returns GraphQL query for retrieving a product with customizable options + * + * @param string $sku + * @return string + */ + private function getProductQuery(string $sku): string + { + $sku = $options = $values = null; // WTF? + return << Date: Wed, 29 Jul 2020 11:56:11 +0200 Subject: [PATCH 17/37] Added test case for adding available QTY + 1 --- .../api-functional/phpunit_graphql.xml.dist | 115 ------------------ ...dSimpleProductToCartSingleMutationTest.php | 47 ++++++- 2 files changed, 43 insertions(+), 119 deletions(-) delete mode 100644 dev/tests/api-functional/phpunit_graphql.xml.dist diff --git a/dev/tests/api-functional/phpunit_graphql.xml.dist b/dev/tests/api-functional/phpunit_graphql.xml.dist deleted file mode 100644 index 2f6ad1f9a37d4..0000000000000 --- a/dev/tests/api-functional/phpunit_graphql.xml.dist +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - - testsuite/Magento/WebApiTest.php - - - testsuite/Magento/GraphQl - - - - - - ./testsuite - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - var/allure-results - true - - - codingStandardsIgnoreStart - - - codingStandardsIgnoreEnd - - - expectedExceptionMessageRegExp - - - magentoAdminConfigFixture - - - magentoAppArea - - - magentoAppIsolation - - - magentoCache - - - magentoComponentsDir - - - magentoConfigFixture - - - magentoDataFixture - - - magentoDataFixtureBeforeTransaction - - - magentoDbIsolation - - - magentoIndexerDimensionMode - - - magentoApiDataFixture - - - Override - - - - - - diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php index 2ab0de101a8f0..93959c1afbbaf 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php @@ -73,7 +73,7 @@ public function testAddSimpleProductWithOptions() json_encode($itemOptions) ); - $query = $this->getQuery($maskedQuoteId, $qty, $sku, trim($productOptionsQuery, '{}')); + $query = $this->getAddToCartMutation($maskedQuoteId, $qty, $sku, trim($productOptionsQuery, '{}')); $response = $this->graphQlMutation($query); self::assertArrayHasKey('items', $response['addProductsToCart']['cart']); @@ -109,7 +109,7 @@ public function testAddProductWithWrongSku(string $sku, string $message) { $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); - $query = $this->getQuery($maskedQuoteId, 1, $sku, ''); + $query = $this->getAddToCartMutation($maskedQuoteId, 1, $sku, ''); $response = $this->graphQlMutation($query); self::assertArrayHasKey('userInputErrors', $response['addProductsToCart']); @@ -120,6 +120,34 @@ public function testAddProductWithWrongSku(string $sku, string $message) ); } + /** + * The test covers the case when upon adding available_qty + 1 to the shopping cart, the cart is being + * cleared + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple_without_custom_options.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testAddToCartWithQtyPlusOne() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + $sku = 'simple-2'; + + $query = $this->getAddToCartMutation($maskedQuoteId, 100, $sku, ''); + $response = $this->graphQlMutation($query); + + self::assertEquals(100, $response['addProductsToCart']['cart']['total_quantity']); + + $query = $this->getAddToCartMutation($maskedQuoteId, 1, $sku, ''); + $response = $this->graphQlMutation($query); + + self::assertArrayHasKey('userInputErrors', $response['addProductsToCart']); + self::assertEquals( + 'The requested qty is not available', + $response['addProductsToCart']['userInputErrors'][0]['message'] + ); + self::assertEquals(100, $response['addProductsToCart']['cart']['total_quantity']); + } + /** * @param int $quantity * @param string $message @@ -134,7 +162,7 @@ public function testAddProductWithWrongQuantity(int $quantity, string $message) $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); $sku = 'simple-2'; - $query = $this->getQuery($maskedQuoteId, $quantity, $sku, ''); + $query = $this->getAddToCartMutation($maskedQuoteId, $quantity, $sku, ''); $response = $this->graphQlMutation($query); self::assertArrayHasKey('userInputErrors', $response['addProductsToCart']); self::assertCount(1, $response['addProductsToCart']['userInputErrors']); @@ -188,7 +216,7 @@ public function wrongQuantityDataProvider(): array * @param string $customizableOptions * @return string */ - private function getQuery( + private function getAddToCartMutation( string $maskedQuoteId, int $qty, string $sku, @@ -207,6 +235,7 @@ private function getQuery( ] ) { cart { + total_quantity items { quantity ... on SimpleCartItem { @@ -227,4 +256,14 @@ private function getQuery( } MUTATION; } + + private function getCartQuery(string $maskedQuoteId) + { + return << Date: Thu, 30 Jul 2020 10:24:05 +0200 Subject: [PATCH 18/37] Minor fixes for tests --- .../Model/Resolver/AddProductsToCart.php | 2 +- .../api-functional/phpunit_graphql.xml.dist | 115 ++++++++++++++++++ ...dSimpleProductToCartSingleMutationTest.php | 2 +- 3 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 dev/tests/api-functional/phpunit_graphql.xml.dist diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/AddProductsToCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/AddProductsToCart.php index de89f3c062e95..a00633390eff1 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/AddProductsToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/AddProductsToCart.php @@ -78,7 +78,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value 'cart' => [ 'model' => $addProductsToCartOutput->getCart(), ], - 'userInputErrors' => \array_map( + 'userInputErrors' => array_map( function (Error $error) { return [ 'code' => $error->getCode(), diff --git a/dev/tests/api-functional/phpunit_graphql.xml.dist b/dev/tests/api-functional/phpunit_graphql.xml.dist new file mode 100644 index 0000000000000..2f6ad1f9a37d4 --- /dev/null +++ b/dev/tests/api-functional/phpunit_graphql.xml.dist @@ -0,0 +1,115 @@ + + + + + + + testsuite/Magento/WebApiTest.php + + + testsuite/Magento/GraphQl + + + + + + ./testsuite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + var/allure-results + true + + + codingStandardsIgnoreStart + + + codingStandardsIgnoreEnd + + + expectedExceptionMessageRegExp + + + magentoAdminConfigFixture + + + magentoAppArea + + + magentoAppIsolation + + + magentoCache + + + magentoComponentsDir + + + magentoConfigFixture + + + magentoDataFixture + + + magentoDataFixtureBeforeTransaction + + + magentoDbIsolation + + + magentoIndexerDimensionMode + + + magentoApiDataFixture + + + Override + + + + + + diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php index 93959c1afbbaf..2e26d89c0b600 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php @@ -77,7 +77,7 @@ public function testAddSimpleProductWithOptions() $response = $this->graphQlMutation($query); self::assertArrayHasKey('items', $response['addProductsToCart']['cart']); - self::assertCount($qty, $response['addProductsToCart']['cart']); + self::assertCount($qty, $response['addProductsToCart']['cart']['items']); $customizableOptionsOutput = $response['addProductsToCart']['cart']['items'][0]['customizable_options']; From b26a7e527fc2682e3a8e6c895133e2cd9f2dcefa Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Thu, 30 Jul 2020 10:34:55 +0200 Subject: [PATCH 19/37] Rename ID_V2 to UID --- ...ddConfigurableProductToCartSingleMutationTest.php | 12 ++++++------ ...ddDownloadableProductToCartSingleMutationTest.php | 2 +- .../Quote/GetCustomOptionsWithUIDForQueryBySku.php | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php index 9b4e1dc6ea610..740c50916e498 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php @@ -43,7 +43,7 @@ public function testAddConfigurableProductToCart() $attributeId = (int) $product['configurable_options'][0]['attribute_id']; $valueIndex = $product['configurable_options'][0]['values'][1]['value_index']; - $selectedConfigurableOptionsQuery = $this->generateSuperAttributesIdV2Query($attributeId, $valueIndex); + $selectedConfigurableOptionsQuery = $this->generateSuperAttributesUIDQuery($attributeId, $valueIndex); $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); $query = $this->getQuery( @@ -68,13 +68,13 @@ public function testAddConfigurableProductToCart() } /** - * Generates Id_v2 for super configurable product super attributes + * Generates UID for super configurable product super attributes * * @param int $attributeId * @param int $valueIndex * @return string */ - private function generateSuperAttributesIdV2Query(int $attributeId, int $valueIndex): string + private function generateSuperAttributesUIDQuery(int $attributeId, int $valueIndex): string { return 'selected_options: ["' . base64_encode("configurable/$attributeId/$valueIndex") . '"]'; } @@ -89,7 +89,7 @@ public function testAddConfigurableProductWithWrongSuperAttributes() $quantity = 2; $parentSku = $product['sku']; - $selectedConfigurableOptionsQuery = $this->generateSuperAttributesIdV2Query(0, 0); + $selectedConfigurableOptionsQuery = $this->generateSuperAttributesUIDQuery(0, 0); $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); $query = $this->getQuery( @@ -118,7 +118,7 @@ public function testAddProductIfQuantityIsNotAvailable() $attributeId = (int) $product['configurable_options'][0]['attribute_id']; $valueIndex = $product['configurable_options'][0]['values'][1]['value_index']; - $selectedConfigurableOptionsQuery = $this->generateSuperAttributesIdV2Query($attributeId, $valueIndex); + $selectedConfigurableOptionsQuery = $this->generateSuperAttributesUIDQuery($attributeId, $valueIndex); $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); $query = $this->getQuery( @@ -171,7 +171,7 @@ public function testOutOfStockVariationToCart() $valueIndex = $product['configurable_options'][0]['values'][0]['value_index']; $parentSku = $product['sku']; - $configurableOptionsQuery = $this->generateSuperAttributesIdV2Query($attributeId, $valueIndex); + $configurableOptionsQuery = $this->generateSuperAttributesUIDQuery($attributeId, $valueIndex); $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); $query = $this->getQuery( diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddDownloadableProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddDownloadableProductToCartSingleMutationTest.php index b457b49a97807..ce613d2d958c0 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddDownloadableProductToCartSingleMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddDownloadableProductToCartSingleMutationTest.php @@ -132,7 +132,7 @@ private function getProductsLinks(string $sku) : array } /** - * Generates Id_v2 for downloadable links + * Generates UID for downloadable links * * @param int $linkId * @return string diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCustomOptionsWithUIDForQueryBySku.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCustomOptionsWithUIDForQueryBySku.php index dcad6a6125c2f..c6377c47b4743 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCustomOptionsWithUIDForQueryBySku.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCustomOptionsWithUIDForQueryBySku.php @@ -10,7 +10,7 @@ use Magento\Catalog\Api\ProductCustomOptionRepositoryInterface; /** - * Generate an array with test values for customizable options with encoded id_v2 value + * Generate an array with test values for customizable options with UID */ class GetCustomOptionsWithUIDForQueryBySku { @@ -83,7 +83,7 @@ public function execute(string $sku): array } /** - * Returns id_v2 of the selected custom option + * Returns UID of the selected custom option * * @param int $optionId * @param int $optionValueId @@ -95,7 +95,7 @@ private function encodeSelectedOption(int $optionId, int $optionValueId): string } /** - * Returns id_v2 of the entered custom option + * Returns UID of the entered custom option * * @param int $optionId * @return string From 633ef00a14ff3d074a6e009a32aac646aa8d22e3 Mon Sep 17 00:00:00 2001 From: Prabhu Ram Date: Thu, 30 Jul 2020 15:29:17 -0500 Subject: [PATCH 20/37] added support for adding bundle options with custom quantity - added e2e tests for adding bundle options ith custom quantity --- .../Cart/BuyRequest/BundleDataProvider.php | 15 ++ ...dBundleProductToCartSingleMutationTest.php | 126 ++++++++++++++++ ...h_multiple_options_and_custom_quantity.php | 137 ++++++++++++++++++ ...e_options_and_custom_quantity_rollback.php | 30 ++++ 4 files changed, 308 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_multiple_options_and_custom_quantity.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_multiple_options_and_custom_quantity_rollback.php diff --git a/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php b/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php index 6e8c9844b1503..01239e50e70b4 100644 --- a/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php +++ b/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php @@ -40,6 +40,21 @@ public function execute(CartItem $cartItem): array $bundleOptionsData['bundle_option'][$optionId] = $optionValueId; $bundleOptionsData['bundle_option_qty'][$optionId] = $optionQuantity; } + //for bundle options with custom quantity + foreach ($cartItem->getEnteredOptions() as $option) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $optionData = \explode('/', base64_decode($option->getId())); + + if ($this->isProviderApplicable($optionData) === false) { + continue; + } + $this->validateInput($optionData); + + [, $optionId, $optionValueId] = $optionData; + $optionQuantity = $option->getValue(); + $bundleOptionsData['bundle_option'][$optionId] = $optionValueId; + $bundleOptionsData['bundle_option_qty'][$optionId] = $optionQuantity; + } return $bundleOptionsData; } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php index 33f2174134b3a..20f2fe3479dcd 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php @@ -222,4 +222,130 @@ public function testAddBundleToCartWithWrongBundleOptions() $response['addProductsToCart']['userInputErrors'][0]['message'] ); } + + /** + * @magentoApiDataFixture Magento/Bundle/_files/product_with_multiple_options_and_custom_quantity.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testUpdateBundleItemWithCustomOptionQuantity() + { + + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $response = $this->graphQlQuery($this->getProductQuery("bundle-product")); + $bundleItem = $response['products']['items'][0]; + $sku = $bundleItem['sku']; + $bundleOptions = $bundleItem['items']; + + $uId0 = $bundleOptions[0]['options'][0]['uid']; + $uId1 = $bundleOptions[1]['options'][0]['uid']; + $response = $this->graphQlMutation( + $this->getMutationsQuery($maskedQuoteId, $uId0, $uId1, $sku) + ); + $bundleOptions = $response['addProductsToCart']['cart']['items'][0]['bundle_options']; + $this->assertEquals(5, $bundleOptions[0]['values'][0]['quantity']); + $this->assertEquals(1, $bundleOptions[1]['values'][0]['quantity']); + } + + /** + * Returns GraphQL query for retrieving a product with customizable options + * + * @param string $sku + * @return string + */ + private function getProductQuery(string $sku): string + { + return <<requireDataFixture('Magento/Catalog/_files/multiple_products.php'); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$productIds = range(10, 12, 1); +foreach ($productIds as $productId) { + /** @var \Magento\CatalogInventory\Model\Stock\Item $stockItem */ + $stockItem = $objectManager->create(\Magento\CatalogInventory\Model\Stock\Item::class); + $stockItem->load($productId, 'product_id'); + + if (!$stockItem->getProductId()) { + $stockItem->setProductId($productId); + } + $stockItem->setUseConfigManageStock(1); + $stockItem->setQty(1000); + $stockItem->setIsQtyDecimal(0); + $stockItem->setIsInStock(1); + $stockItem->save(); +} + +/** @var $product \Magento\Catalog\Model\Product */ +$product = $objectManager->create(\Magento\Catalog\Model\Product::class); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE) + ->setId(3) + ->setAttributeSetId(4) + ->setName('Bundle Product') + ->setSku('bundle-product') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setWebsiteIds([1]) + ->setPriceType(1) + ->setPrice(10.0) + ->setShipmentType(0) + ->setPriceView(1) + ->setBundleOptionsData( + [ + // Required "Drop-down" option + [ + 'title' => 'Option 1', + 'default_title' => 'Option 1', + 'type' => 'select', + 'required' => 1, + 'position' => 1, + 'delete' => '', + ], + // Required "Radio Buttons" option + [ + 'title' => 'Option 2', + 'default_title' => 'Option 2', + 'type' => 'radio', + 'required' => 1, + 'position' => 2, + 'delete' => '', + ], + ] + )->setBundleSelectionsData( + [ + [ + [ + 'product_id' => 10, + 'selection_qty' => 1, + 'selection_can_change_qty' => 1, + 'delete' => '', + 'option_id' => 1 + ], + [ + 'product_id' => 11, + 'selection_qty' => 1, + 'selection_can_change_qty' => 1, + 'delete' => '', + 'option_id' => 1 + ] + ], + [ + [ + 'product_id' => 10, + 'selection_qty' => 1, + 'selection_can_change_qty' => 0, + 'delete' => '', + 'option_id' => 2 + ], + [ + 'product_id' => 11, + 'selection_qty' => 1, + 'selection_can_change_qty' => 0, + 'delete' => '', + 'option_id' => 2 + ] + ] + ] + ); +$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +if ($product->getBundleOptionsData()) { + $options = []; + foreach ($product->getBundleOptionsData() as $key => $optionData) { + if (!(bool)$optionData['delete']) { + $option = $objectManager->create(\Magento\Bundle\Api\Data\OptionInterfaceFactory::class) + ->create(['data' => $optionData]); + $option->setSku($product->getSku()); + $option->setOptionId(null); + + $links = []; + $bundleLinks = $product->getBundleSelectionsData(); + if (!empty($bundleLinks[$key])) { + foreach ($bundleLinks[$key] as $linkData) { + if (!(bool)$linkData['delete']) { + $link = $objectManager->create(\Magento\Bundle\Api\Data\LinkInterfaceFactory::class) + ->create(['data' => $linkData]); + $linkProduct = $productRepository->getById($linkData['product_id']); + $link->setSku($linkProduct->getSku()); + $link->setQty($linkData['selection_qty']); + if (isset($linkData['selection_can_change_qty'])) { + $link->setCanChangeQuantity($linkData['selection_can_change_qty']); + } + $links[] = $link; + } + } + $option->setProductLinks($links); + $options[] = $option; + } + } + } + $extension = $product->getExtensionAttributes(); + $extension->setBundleProductOptions($options); + $product->setExtensionAttributes($extension); +} +$product->save(); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_multiple_options_and_custom_quantity_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_multiple_options_and_custom_quantity_rollback.php new file mode 100644 index 0000000000000..9d702b4506551 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_multiple_options_and_custom_quantity_rollback.php @@ -0,0 +1,30 @@ +requireDataFixture('Magento/Catalog/_files/multiple_products_rollback.php'); + +$objectManager = Bootstrap::getObjectManager(); +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); +/** @var \Magento\Framework\Registry $registry */ +$registry = Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + $product = $productRepository->get('bundle-product'); + $productRepository->delete($product); +} catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From 38504fd0668f749330ef235b25634ba41f5e12d4 Mon Sep 17 00:00:00 2001 From: Prabhu Ram Date: Thu, 30 Jul 2020 21:53:04 -0500 Subject: [PATCH 21/37] minor fix on tests --- .../Bundle/AddBundleProductToCartSingleMutationTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php index 20f2fe3479dcd..be1bfce9441d6 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php @@ -227,7 +227,7 @@ public function testAddBundleToCartWithWrongBundleOptions() * @magentoApiDataFixture Magento/Bundle/_files/product_with_multiple_options_and_custom_quantity.php * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php */ - public function testUpdateBundleItemWithCustomOptionQuantity() + public function testAddBundleItemWithCustomOptionQuantity() { $this->quoteResource->load( @@ -309,11 +309,11 @@ private function getMutationsQuery( "{$optionUid1}", "{$optionUid0}" ], entered_options: [{ - id: "{$optionUid0}" + uid: "{$optionUid0}" value: "5" }, { - id: "{$optionUid1}" + uid: "{$optionUid1}" value: "5" }] } From 394dd7e4f8ce7a8d4aeb322ad3c4b2165f3317e1 Mon Sep 17 00:00:00 2001 From: Prabhu Ram Date: Thu, 30 Jul 2020 21:55:05 -0500 Subject: [PATCH 22/37] minor fix --- .../Bundle/AddBundleProductToCartSingleMutationTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php index be1bfce9441d6..3fb183b3016c5 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php @@ -309,11 +309,11 @@ private function getMutationsQuery( "{$optionUid1}", "{$optionUid0}" ], entered_options: [{ - uid: "{$optionUid0}" + id: "{$optionUid0}" value: "5" }, { - uid: "{$optionUid1}" + id: "{$optionUid1}" value: "5" }] } From 06e3c7bcf1d53568292335e3ea1c0a3c2b23cc89 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Fri, 31 Jul 2020 16:11:30 +0200 Subject: [PATCH 23/37] Save quote if there are no errors only --- app/code/Magento/Quote/Model/Cart/AddProductsToCart.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php index 26df021e6325b..c80807b93c8b0 100644 --- a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php @@ -105,6 +105,7 @@ public function execute(string $maskedCartId, array $cartItems): AddProductsToCa foreach ($cartItems as $cartItemPosition => $cartItem) { $this->addItemToCart($cart, $cartItem, $cartItemPosition); } + if ($cart->getData('has_error')) { $errors = $cart->getErrors(); @@ -113,7 +114,10 @@ public function execute(string $maskedCartId, array $cartItems): AddProductsToCa $this->addError($error->getText()); } } - $this->cartRepository->save($cart); + + if (count($this->errors) === 0) { + $this->cartRepository->save($cart); + } return $this->prepareErrorOutput($cart); } From c24c12b609eb74c10ffb1f6eda12b06a93bc6dbc Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Sun, 2 Aug 2020 14:04:52 +0200 Subject: [PATCH 24/37] Revert quote items in case of an error --- app/code/Magento/Quote/Model/Cart/AddProductsToCart.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php index c80807b93c8b0..8707f7755e2fb 100644 --- a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php @@ -117,6 +117,9 @@ public function execute(string $maskedCartId, array $cartItems): AddProductsToCa if (count($this->errors) === 0) { $this->cartRepository->save($cart); + } else { + /* Revert changes introduced by add to cart processes in case of an error */ + $cart->getItemsCollection()->clear(); } return $this->prepareErrorOutput($cart); From f91b685597c00c2f7e3468a887e956dbee7d02a8 Mon Sep 17 00:00:00 2001 From: Prabhu Ram Date: Mon, 3 Aug 2020 23:46:51 -0500 Subject: [PATCH 25/37] static test fixes --- .../CustomizableOptionDataProvider.php | 12 ++++++++---- .../Cart/BuyRequest/BundleDataProvider.php | 18 +++++++++++------- .../BuyRequest/SuperAttributeDataProvider.php | 6 ++++-- .../DownloadableLinkDataProvider.php | 6 ++++-- .../AddSimpleProductToCartEndToEndTest.php | 4 +--- ...dSimpleProductToCartSingleMutationTest.php | 10 ---------- .../Quote/GetCartItemOptionsFromUID.php | 19 +++++++++++-------- 7 files changed, 39 insertions(+), 36 deletions(-) diff --git a/app/code/Magento/Quote/Model/Cart/BuyRequest/CustomizableOptionDataProvider.php b/app/code/Magento/Quote/Model/Cart/BuyRequest/CustomizableOptionDataProvider.php index 173f0bb403f62..d5440c272d163 100644 --- a/app/code/Magento/Quote/Model/Cart/BuyRequest/CustomizableOptionDataProvider.php +++ b/app/code/Magento/Quote/Model/Cart/BuyRequest/CustomizableOptionDataProvider.php @@ -35,8 +35,10 @@ public function execute(CartItem $cartItem): array } $this->validateInput($optionData); - [, $optionId, $optionValue] = $optionData; - $customizableOptionsData[$optionId][] = $optionValue; + [$optionType, $optionId, $optionValue] = $optionData; + if ($optionType == self::OPTION_TYPE) { + $customizableOptionsData[$optionId][] = $optionValue; + } } foreach ($cartItem->getEnteredOptions() as $option) { @@ -47,8 +49,10 @@ public function execute(CartItem $cartItem): array continue; } - [, $optionId] = $optionData; - $customizableOptionsData[$optionId][] = $option->getValue(); + [$optionType, $optionId] = $optionData; + if ($optionType == self::OPTION_TYPE) { + $customizableOptionsData[$optionId][] = $option->getValue(); + } } return ['options' => $this->flattenOptionValues($customizableOptionsData)]; diff --git a/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php b/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php index 01239e50e70b4..9c6ed01bd527f 100644 --- a/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php +++ b/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php @@ -36,9 +36,11 @@ public function execute(CartItem $cartItem): array } $this->validateInput($optionData); - [, $optionId, $optionValueId, $optionQuantity] = $optionData; - $bundleOptionsData['bundle_option'][$optionId] = $optionValueId; - $bundleOptionsData['bundle_option_qty'][$optionId] = $optionQuantity; + [$optionType, $optionId, $optionValueId, $optionQuantity] = $optionData; + if ($optionType == self::OPTION_TYPE) { + $bundleOptionsData['bundle_option'][$optionId] = $optionValueId; + $bundleOptionsData['bundle_option_qty'][$optionId] = $optionQuantity; + } } //for bundle options with custom quantity foreach ($cartItem->getEnteredOptions() as $option) { @@ -50,10 +52,12 @@ public function execute(CartItem $cartItem): array } $this->validateInput($optionData); - [, $optionId, $optionValueId] = $optionData; - $optionQuantity = $option->getValue(); - $bundleOptionsData['bundle_option'][$optionId] = $optionValueId; - $bundleOptionsData['bundle_option_qty'][$optionId] = $optionQuantity; + [$optionType, $optionId, $optionValueId] = $optionData; + if ($optionType == self::OPTION_TYPE) { + $optionQuantity = $option->getValue(); + $bundleOptionsData['bundle_option'][$optionId] = $optionValueId; + $bundleOptionsData['bundle_option_qty'][$optionId] = $optionQuantity; + } } return $bundleOptionsData; diff --git a/app/code/Magento/QuoteConfigurableOptions/Model/Cart/BuyRequest/SuperAttributeDataProvider.php b/app/code/Magento/QuoteConfigurableOptions/Model/Cart/BuyRequest/SuperAttributeDataProvider.php index e7d5820be8a59..d58b574352bd8 100644 --- a/app/code/Magento/QuoteConfigurableOptions/Model/Cart/BuyRequest/SuperAttributeDataProvider.php +++ b/app/code/Magento/QuoteConfigurableOptions/Model/Cart/BuyRequest/SuperAttributeDataProvider.php @@ -35,8 +35,10 @@ public function execute(CartItem $cartItem): array } $this->validateInput($optionData); - [, $attributeId, $valueIndex] = $optionData; - $configurableProductData[$attributeId] = $valueIndex; + [$optionType, $attributeId, $valueIndex] = $optionData; + if ($optionType == self::OPTION_TYPE) { + $configurableProductData[$attributeId] = $valueIndex; + } } return ['super_attribute' => $configurableProductData]; diff --git a/app/code/Magento/QuoteDownloadableLinks/Model/Cart/BuyRequest/DownloadableLinkDataProvider.php b/app/code/Magento/QuoteDownloadableLinks/Model/Cart/BuyRequest/DownloadableLinkDataProvider.php index cf381a19e2ddd..e412c7df573c7 100644 --- a/app/code/Magento/QuoteDownloadableLinks/Model/Cart/BuyRequest/DownloadableLinkDataProvider.php +++ b/app/code/Magento/QuoteDownloadableLinks/Model/Cart/BuyRequest/DownloadableLinkDataProvider.php @@ -36,8 +36,10 @@ public function execute(CartItem $cartItem): array } $this->validateInput($optionData); - [, $linkId] = $optionData; - $linksData[] = $linkId; + [$optionType, $linkId] = $optionData; + if ($optionType == self::OPTION_TYPE) { + $linksData[] = $linkId; + } } return ['links' => $linksData]; diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartEndToEndTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartEndToEndTest.php index a03c2eb1ff698..368670f4032a7 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartEndToEndTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartEndToEndTest.php @@ -111,7 +111,6 @@ private function getProductOptionsViaQuery(string $sku): array 'value' => $value ]; - } elseif (isset($option['selected_option'])) { $receivedItemOptions['selected_options'][] = reset($option['selected_option'])['uid']; $expectedItemOptions[$option['option_id']] = reset($option['selected_option'])['option_type_id']; @@ -132,10 +131,9 @@ private function getProductOptionsViaQuery(string $sku): array */ private function getProductQuery(string $sku): string { - $sku = $options = $values = null; // WTF? return << Date: Tue, 4 Aug 2020 15:16:40 +0200 Subject: [PATCH 26/37] Adjusted assertion message --- .../AddConfigurableProductToCartSingleMutationTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php index 740c50916e498..be444d7b22b45 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php @@ -183,7 +183,7 @@ public function testOutOfStockVariationToCart() $response = $this->graphQlMutation($query); - $expectedErrorMessage = 'This product is out of stock.'; + $expectedErrorMessage = 'There are no source items with the in stock status'; self::assertEquals($expectedErrorMessage, $response['addProductsToCart']['userInputErrors'][0]['message']); } From 330e7636116dba5ad870c112455a87eccdfdfb9a Mon Sep 17 00:00:00 2001 From: Prabhu Ram Date: Tue, 4 Aug 2020 15:20:07 -0500 Subject: [PATCH 27/37] - Removed dependency between QuoteGQL and WishlistGQL (https://github.com/magento/architecture/pull/413) - fixed uid field types (https://github.com/magento/architecture/pull/412/) - fixed api functional tests --- app/code/Magento/GraphQl/etc/schema.graphqls | 5 +++++ .../BuyRequest/CustomizableOptionDataProvider.php | 2 +- .../Quote/Model/Cart/Data/CartItemFactory.php | 6 +++--- .../Quote/Model/Cart/Data/EnteredOption.php | 12 ++++++------ .../Model/Cart/BuyRequest/BundleDataProvider.php | 2 +- app/code/Magento/QuoteGraphQl/etc/schema.graphqls | 7 +------ .../BuyRequest/CustomizableOptionDataProvider.php | 14 +++++++++----- .../Wishlist/Model/Wishlist/Data/EnteredOption.php | 12 ++++++------ .../Model/Wishlist/Data/WishlistItemFactory.php | 6 +++--- .../Magento/WishlistGraphQl/etc/schema.graphqls | 9 ++------- .../AddBundleProductToCartSingleMutationTest.php | 4 ++-- ...ConfigurableProductToCartSingleMutationTest.php | 10 ++++++++-- .../Quote/AddSimpleProductToCartEndToEndTest.php | 2 +- .../GraphQl/Quote/GetCartItemOptionsFromUID.php | 4 ++-- .../Quote/GetCustomOptionsWithUIDForQueryBySku.php | 4 ++-- .../GetCustomOptionsWithIDV2ForQueryBySku.php | 2 +- 16 files changed, 53 insertions(+), 48 deletions(-) diff --git a/app/code/Magento/GraphQl/etc/schema.graphqls b/app/code/Magento/GraphQl/etc/schema.graphqls index 0212d32db0f2f..2595ad09c072a 100644 --- a/app/code/Magento/GraphQl/etc/schema.graphqls +++ b/app/code/Magento/GraphQl/etc/schema.graphqls @@ -277,3 +277,8 @@ enum CurrencyEnum @doc(description: "The list of available currency codes") { TRL XPF } + +input EnteredOptionInput @doc(description: "Defines a customer-entered option") { + uid: ID! @doc(description: "An encoded ID") + value: String! @doc(description: "Text the customer entered") +} diff --git a/app/code/Magento/Quote/Model/Cart/BuyRequest/CustomizableOptionDataProvider.php b/app/code/Magento/Quote/Model/Cart/BuyRequest/CustomizableOptionDataProvider.php index d5440c272d163..90f2cbbc5f9e3 100644 --- a/app/code/Magento/Quote/Model/Cart/BuyRequest/CustomizableOptionDataProvider.php +++ b/app/code/Magento/Quote/Model/Cart/BuyRequest/CustomizableOptionDataProvider.php @@ -43,7 +43,7 @@ public function execute(CartItem $cartItem): array foreach ($cartItem->getEnteredOptions() as $option) { // phpcs:ignore Magento2.Functions.DiscouragedFunction - $optionData = \explode('/', base64_decode($option->getId())); + $optionData = \explode('/', base64_decode($option->getUid())); if ($this->isProviderApplicable($optionData) === false) { continue; diff --git a/app/code/Magento/Quote/Model/Cart/Data/CartItemFactory.php b/app/code/Magento/Quote/Model/Cart/Data/CartItemFactory.php index 468d5950bd2dd..823f03b28229c 100644 --- a/app/code/Magento/Quote/Model/Cart/Data/CartItemFactory.php +++ b/app/code/Magento/Quote/Model/Cart/Data/CartItemFactory.php @@ -45,12 +45,12 @@ private function createEnteredOptions(array $options): array { return \array_map( function (array $option) { - if (!isset($option['id'], $option['value'])) { + if (!isset($option['uid'], $option['value'])) { throw new InputException( - __('Required fields are not present EnteredOption.id, EnteredOption.value') + __('Required fields are not present EnteredOption.uid, EnteredOption.value') ); } - return new EnteredOption($option['id'], $option['value']); + return new EnteredOption($option['uid'], $option['value']); }, $options ); diff --git a/app/code/Magento/Quote/Model/Cart/Data/EnteredOption.php b/app/code/Magento/Quote/Model/Cart/Data/EnteredOption.php index b60c349a8e99a..ba55051d33805 100644 --- a/app/code/Magento/Quote/Model/Cart/Data/EnteredOption.php +++ b/app/code/Magento/Quote/Model/Cart/Data/EnteredOption.php @@ -15,7 +15,7 @@ class EnteredOption /** * @var string */ - private $id; + private $uid; /** * @var string @@ -23,12 +23,12 @@ class EnteredOption private $value; /** - * @param string $id + * @param string $uid * @param string $value */ - public function __construct(string $id, string $value) + public function __construct(string $uid, string $value) { - $this->id = $id; + $this->uid = $uid; $this->value = $value; } @@ -37,9 +37,9 @@ public function __construct(string $id, string $value) * * @return string */ - public function getId(): string + public function getUid(): string { - return $this->id; + return $this->uid; } /** diff --git a/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php b/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php index 9c6ed01bd527f..575784c86ace1 100644 --- a/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php +++ b/app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php @@ -45,7 +45,7 @@ public function execute(CartItem $cartItem): array //for bundle options with custom quantity foreach ($cartItem->getEnteredOptions() as $option) { // phpcs:ignore Magento2.Functions.DiscouragedFunction - $optionData = \explode('/', base64_decode($option->getId())); + $optionData = \explode('/', base64_decode($option->getUid())); if ($this->isProviderApplicable($optionData) === false) { continue; diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 7dae2987125e9..6a96f361a90b0 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -53,7 +53,7 @@ input CartItemInput { sku: String! quantity: Float! parent_sku: String, - selected_options: [String!] + selected_options: [ID!] entered_options: [EnteredOptionInput!] } @@ -319,11 +319,6 @@ type SetGuestEmailOnCartOutput { cart: Cart! } -input EnteredOptionInput { - id: ID! @doc(description: "base64 encoded option ID") - value: String! -} - type SimpleCartItem implements CartItemInterface @doc(description: "Simple Cart Item") { customizable_options: [SelectedCustomizableOption] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\CustomizableOptions") } diff --git a/app/code/Magento/Wishlist/Model/Wishlist/BuyRequest/CustomizableOptionDataProvider.php b/app/code/Magento/Wishlist/Model/Wishlist/BuyRequest/CustomizableOptionDataProvider.php index e8f5bf0654f64..8bf12206336a8 100644 --- a/app/code/Magento/Wishlist/Model/Wishlist/BuyRequest/CustomizableOptionDataProvider.php +++ b/app/code/Magento/Wishlist/Model/Wishlist/BuyRequest/CustomizableOptionDataProvider.php @@ -31,21 +31,25 @@ public function execute(WishlistItem $wishlistItemData, ?int $productId): array continue; } - [, $optionId, $optionValue] = $optionData; + [$optionType, $optionId, $optionValue] = $optionData; - $customizableOptionsData[$optionId][] = $optionValue; + if ($optionType == self::PROVIDER_OPTION_TYPE) { + $customizableOptionsData[$optionId][] = $optionValue; + } } foreach ($wishlistItemData->getEnteredOptions() as $option) { - $optionData = \explode('/', base64_decode($option->getId())); + $optionData = \explode('/', base64_decode($option->getUid())); if ($this->isProviderApplicable($optionData) === false) { continue; } - [, $optionId] = $optionData; + [$optionType, $optionId] = $optionData; - $customizableOptionsData[$optionId][] = $option->getValue(); + if ($optionType == self::PROVIDER_OPTION_TYPE) { + $customizableOptionsData[$optionId][] = $option->getValue(); + } } if (empty($customizableOptionsData)) { diff --git a/app/code/Magento/Wishlist/Model/Wishlist/Data/EnteredOption.php b/app/code/Magento/Wishlist/Model/Wishlist/Data/EnteredOption.php index 0d6b2a2302540..edbf84781da38 100644 --- a/app/code/Magento/Wishlist/Model/Wishlist/Data/EnteredOption.php +++ b/app/code/Magento/Wishlist/Model/Wishlist/Data/EnteredOption.php @@ -15,7 +15,7 @@ class EnteredOption /** * @var string */ - private $id; + private $uid; /** * @var string @@ -23,12 +23,12 @@ class EnteredOption private $value; /** - * @param string $id + * @param string $uid * @param string $value */ - public function __construct(string $id, string $value) + public function __construct(string $uid, string $value) { - $this->id = $id; + $this->uid = $uid; $this->value = $value; } @@ -37,9 +37,9 @@ public function __construct(string $id, string $value) * * @return string */ - public function getId(): string + public function getUid(): string { - return $this->id; + return $this->uid; } /** diff --git a/app/code/Magento/Wishlist/Model/Wishlist/Data/WishlistItemFactory.php b/app/code/Magento/Wishlist/Model/Wishlist/Data/WishlistItemFactory.php index 153e8451bae31..aef3cbf571ff6 100644 --- a/app/code/Magento/Wishlist/Model/Wishlist/Data/WishlistItemFactory.php +++ b/app/code/Magento/Wishlist/Model/Wishlist/Data/WishlistItemFactory.php @@ -45,12 +45,12 @@ private function createEnteredOptions(array $options): array { return \array_map( function (array $option) { - if (!isset($option['id'], $option['value'])) { + if (!isset($option['uid'], $option['value'])) { throw new InputException( - __('Required fields are not present EnteredOption.id, EnteredOption.value') + __('Required fields are not present EnteredOption.uid, EnteredOption.value') ); } - return new EnteredOption($option['id'], $option['value']); + return new EnteredOption($option['uid'], $option['value']); }, $options ); diff --git a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls index 794e90ed9f9a9..c6993367ffbe3 100644 --- a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls +++ b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls @@ -43,7 +43,7 @@ input WishlistItemInput @doc(description: "Defines the items to add to a wish li sku: String @doc(description: "The SKU of the product to add. For complex product types, specify the child product SKU") quantity: Float @doc(description: "The amount or number of items to add") parent_sku: String @doc(description: "For complex product types, the SKU of the parent product") - selected_options: [String!] @doc(description: "An array of strings corresponding to options the customer selected") + selected_options: [ID!] @doc(description: "An array of strings corresponding to options the customer selected") entered_options: [EnteredOptionInput!] @doc(description: "An array of options that the customer entered") } @@ -52,11 +52,6 @@ type AddProductsToWishlistOutput @doc(description: "Contains the customer's wish userInputErrors:[CheckoutUserInputError]! @doc(description: "An array of errors encountered while adding products to a wish list") } -input EnteredOptionInput @doc(description: "Defines a customer-entered option") { - id: String! @doc(description: "A base64 encoded ID") - value: String! @doc(description: "Text the customer entered") -} - type RemoveProductsFromWishlistOutput @doc(description: "Contains the customer's wish list and any errors encountered") { wishlist: Wishlist! @doc(description: "Contains the wish list with after items were successfully deleted") userInputErrors:[CheckoutUserInputError]! @doc(description:"An array of errors encountered while deleting products from a wish list") @@ -66,7 +61,7 @@ input WishlistItemUpdateInput @doc(description: "Defines updates to items in a w wishlist_item_id: ID @doc(description: "The ID of the wishlist item to update") quantity: Float @doc(description: "The new amount or number of this item") description: String @doc(description: "Describes the update") - selected_options: [String!] @doc(description: "An array of strings corresponding to options the customer selected") + selected_options: [ID!] @doc(description: "An array of strings corresponding to options the customer selected") entered_options: [EnteredOptionInput!] @doc(description: "An array of options that the customer entered") } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php index 3fb183b3016c5..be1bfce9441d6 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php @@ -309,11 +309,11 @@ private function getMutationsQuery( "{$optionUid1}", "{$optionUid0}" ], entered_options: [{ - id: "{$optionUid0}" + uid: "{$optionUid0}" value: "5" }, { - id: "{$optionUid1}" + uid: "{$optionUid1}" value: "5" }] } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php index be444d7b22b45..3612bc7f26e49 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php @@ -183,8 +183,14 @@ public function testOutOfStockVariationToCart() $response = $this->graphQlMutation($query); - $expectedErrorMessage = 'There are no source items with the in stock status'; - self::assertEquals($expectedErrorMessage, $response['addProductsToCart']['userInputErrors'][0]['message']); + $expectedErrorMessages = [ + 'There are no source items with the in stock status', + 'This product is out of stock.' + ]; + $this->assertContains( + $response['addProductsToCart']['userInputErrors'][0]['message'], + $expectedErrorMessages + ); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartEndToEndTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartEndToEndTest.php index 368670f4032a7..f416dad46f64a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartEndToEndTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartEndToEndTest.php @@ -107,7 +107,7 @@ private function getProductOptionsViaQuery(string $sku): array $value = $option['title'] === 'date option' ? '2012-12-12 00:00:00' : 'test'; $receivedItemOptions['entered_options'][] = [ - 'id' => $option['entered_option']['uid'], + 'uid' => $option['entered_option']['uid'], 'value' => $value ]; diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartItemOptionsFromUID.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartItemOptionsFromUID.php index 1f492c8b918c5..44b44e0ccac05 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartItemOptionsFromUID.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartItemOptionsFromUID.php @@ -38,8 +38,8 @@ public function execute(array $encodedCustomOptions): array if ($enteredOption['type'] === 'date') { $enteredOption['value'] = date('M d, Y', strtotime($enteredOption['value'])); } - [$optionType, $optionId] = explode('/', base64_decode($enteredOption['id'])); - if ($optionType = 'custom-option') { + [$optionType, $optionId] = explode('/', base64_decode($enteredOption['uid'])); + if ($optionType == 'custom-option') { $customOptions[$optionId] = $enteredOption['value']; } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCustomOptionsWithUIDForQueryBySku.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCustomOptionsWithUIDForQueryBySku.php index c6377c47b4743..870617555e8b2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCustomOptionsWithUIDForQueryBySku.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCustomOptionsWithUIDForQueryBySku.php @@ -47,14 +47,14 @@ public function execute(string $sku): array case 'area': $enteredOptions[] = [ 'type' => 'field', - 'id' => $this->encodeEnteredOption((int) $customOption->getOptionId()), + 'uid' => $this->encodeEnteredOption((int) $customOption->getOptionId()), 'value' => 'test' ]; break; case 'date': $enteredOptions[] = [ 'type' => 'date', - 'id' => $this->encodeEnteredOption((int) $customOption->getOptionId()), + 'uid' => $this->encodeEnteredOption((int) $customOption->getOptionId()), 'value' => '2012-12-12 00:00:00' ]; break; diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/GetCustomOptionsWithIDV2ForQueryBySku.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/GetCustomOptionsWithIDV2ForQueryBySku.php index 6d54d9f0b4444..fcba7458f317a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/GetCustomOptionsWithIDV2ForQueryBySku.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/GetCustomOptionsWithIDV2ForQueryBySku.php @@ -45,7 +45,7 @@ public function execute(string $sku): array if ($optionType === 'field' || $optionType === 'area' || $optionType === 'date') { $enteredOptions[] = [ - 'id' => $this->encodeEnteredOption((int)$customOption->getOptionId()), + 'uid' => $this->encodeEnteredOption((int)$customOption->getOptionId()), 'value' => '2012-12-12' ]; } elseif ($optionType === 'drop_down') { From f6cfcff5e2f372430f53fe7a22eae322a7c0573d Mon Sep 17 00:00:00 2001 From: Prabhu Ram Date: Tue, 4 Aug 2020 17:34:05 -0500 Subject: [PATCH 28/37] - Updated schema descriptions --- .../Magento/QuoteGraphQl/etc/schema.graphqls | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 6a96f361a90b0..2c6fca1a14b23 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -22,7 +22,7 @@ type Mutation { setPaymentMethodAndPlaceOrder(input: SetPaymentMethodAndPlaceOrderInput): PlaceOrderOutput @deprecated(reason: "Should use setPaymentMethodOnCart and placeOrder mutations in single request.") @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetPaymentAndPlaceOrder") mergeCarts(source_cart_id: String!, destination_cart_id: String!): Cart! @doc(description:"Merges the source cart into the destination cart") @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\MergeCarts") placeOrder(input: PlaceOrderInput): PlaceOrderOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\PlaceOrder") - addProductsToCart(cartId: String!, cartItems: [CartItemInput!]!): AddProductsToCartOutput @doc(description:"Add products to the cart. Provide single interface for all product types ") @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddProductsToCart") + addProductsToCart(cartId: String!, cartItems: [CartItemInput!]!): AddProductsToCartOutput @doc(description:"Add any type of product to the cart") @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddProductsToCart") } input createEmptyCartInput { @@ -52,9 +52,9 @@ input VirtualProductCartItemInput { input CartItemInput { sku: String! quantity: Float! - parent_sku: String, - selected_options: [ID!] - entered_options: [EnteredOptionInput!] + parent_sku: String @doc(description: "For child products, the SKU of its parent product") + selected_options: [ID!] @doc(description: "The selected options for the base product, such as color or size") + entered_options: [EnteredOptionInput!] @doc(description: "An array of entered options for the base product, such as personalization text") } input CustomizableOptionInput { @@ -373,15 +373,15 @@ type Order { order_id: String @deprecated(reason: "The order_id field is deprecated, use order_number instead.") } -type CheckoutUserInputError @doc(description:"An error encountered while adding an item the the cart.") { - message: String! @doc(description: "Localized error message") +type CheckoutUserInputError @doc(description:"An error encountered while adding an item to the the cart.") { + message: String! @doc(description: "A localized error message") path: [String]! @doc(description: "Path to the input field that caused an error. See the GraphQL specification about path errors for details: http://spec.graphql.org/draft/#sec-Errors") code: CheckoutUserInputErrorCodes! @doc(description: "Checkout-specific error code") } type AddProductsToCartOutput { - cart: Cart! - userInputErrors:[CheckoutUserInputError]! + cart: Cart! @doc(description: "The cart after products have been added") + userInputErrors:[CheckoutUserInputError]! @doc(description: "An error encountered while adding an item to the cart.") } enum CheckoutUserInputErrorCodes { From b1159bfcf42bffc284b401b7b474a40371bc5388 Mon Sep 17 00:00:00 2001 From: Prabhu Ram Date: Tue, 4 Aug 2020 18:53:34 -0500 Subject: [PATCH 29/37] - Fixed/ Removed dependencies in schema between QuoteGQL, SalesGQL and WishlistGQL --- .../Magento/QuoteGraphQl/etc/schema.graphqls | 8 ++++---- .../Magento/WishlistGraphQl/etc/schema.graphqls | 17 ++++++++++++++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 2c6fca1a14b23..a01afabba9683 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -373,18 +373,18 @@ type Order { order_id: String @deprecated(reason: "The order_id field is deprecated, use order_number instead.") } -type CheckoutUserInputError @doc(description:"An error encountered while adding an item to the the cart.") { +type CartUserInputError @doc(description:"An error encountered while adding an item to the the cart.") { message: String! @doc(description: "A localized error message") path: [String]! @doc(description: "Path to the input field that caused an error. See the GraphQL specification about path errors for details: http://spec.graphql.org/draft/#sec-Errors") - code: CheckoutUserInputErrorCodes! @doc(description: "Checkout-specific error code") + code: CartUserInputErrorType! @doc(description: "Checkout-specific error code") } type AddProductsToCartOutput { cart: Cart! @doc(description: "The cart after products have been added") - userInputErrors:[CheckoutUserInputError]! @doc(description: "An error encountered while adding an item to the cart.") + userInputErrors:[CartUserInputError]! @doc(description: "An error encountered while adding an item to the cart.") } -enum CheckoutUserInputErrorCodes { +enum CartUserInputErrorType { PRODUCT_NOT_FOUND NOT_SALABLE INSUFFICIENT_STOCK diff --git a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls index c6993367ffbe3..702821fd7af92 100644 --- a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls +++ b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls @@ -49,12 +49,12 @@ input WishlistItemInput @doc(description: "Defines the items to add to a wish li type AddProductsToWishlistOutput @doc(description: "Contains the customer's wish list and any errors encountered") { wishlist: Wishlist! @doc(description: "Contains the wish list with all items that were successfully added") - userInputErrors:[CheckoutUserInputError]! @doc(description: "An array of errors encountered while adding products to a wish list") + userInputErrors:[WishListUserInputError]! @doc(description: "An array of errors encountered while adding products to a wish list") } type RemoveProductsFromWishlistOutput @doc(description: "Contains the customer's wish list and any errors encountered") { wishlist: Wishlist! @doc(description: "Contains the wish list with after items were successfully deleted") - userInputErrors:[CheckoutUserInputError]! @doc(description:"An array of errors encountered while deleting products from a wish list") + userInputErrors:[WishListUserInputError]! @doc(description:"An array of errors encountered while deleting products from a wish list") } input WishlistItemUpdateInput @doc(description: "Defines updates to items in a wish list") { @@ -67,5 +67,16 @@ input WishlistItemUpdateInput @doc(description: "Defines updates to items in a w type UpdateProductsInWishlistOutput @doc(description: "Contains the customer's wish list and any errors encountered") { wishlist: Wishlist! @doc(description: "Contains the wish list with all items that were successfully updated") - userInputErrors:[CheckoutUserInputError]! @doc(description:"An array of errors encountered while updating products in a wish list") + userInputErrors:[WishListUserInputError]! @doc(description:"An array of errors encountered while updating products in a wish list") +} + +type WishListUserInputError @doc(description:"An error encountered while performing operations with WishList.") { + message: String! @doc(description: "A localized error message") + path: [String]! @doc(description: "Path to the input field that caused an error. See the GraphQL specification about path errors for details: http://spec.graphql.org/draft/#sec-Errors") + code: WishListUserInputErrorType! @doc(description: "Wishlist-specific error code") +} + +enum WishListUserInputErrorType { + PRODUCT_NOT_FOUND + UNDEFINED } From 8b925df01dc81e2f55fe378ad54d15e2a8c128f6 Mon Sep 17 00:00:00 2001 From: Prabhu Ram Date: Tue, 4 Aug 2020 21:55:01 -0500 Subject: [PATCH 30/37] - reverting changes in Sales GraphQL --- app/code/Magento/SalesGraphQl/etc/schema.graphqls | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/SalesGraphQl/etc/schema.graphqls b/app/code/Magento/SalesGraphQl/etc/schema.graphqls index 717e9c1a00841..218619e0ced34 100644 --- a/app/code/Magento/SalesGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SalesGraphQl/etc/schema.graphqls @@ -224,4 +224,8 @@ type KeyValue @doc(description: "The key-value type") { enum CheckoutUserInputErrorCodes { REORDER_NOT_AVAILABLE + PRODUCT_NOT_FOUND + NOT_SALABLE + INSUFFICIENT_STOCK + UNDEFINED } From 4c5403d5303e6472e0604da9fc6209a4713c3177 Mon Sep 17 00:00:00 2001 From: Prabhu Ram Date: Wed, 5 Aug 2020 14:56:36 -0500 Subject: [PATCH 31/37] - removing path from UserInputErrors since its not in scope and current implementation is irrelevant. --- app/code/Magento/QuoteGraphQl/etc/schema.graphqls | 1 - app/code/Magento/WishlistGraphQl/etc/schema.graphqls | 1 - 2 files changed, 2 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index a01afabba9683..5d11d93eedf85 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -375,7 +375,6 @@ type Order { type CartUserInputError @doc(description:"An error encountered while adding an item to the the cart.") { message: String! @doc(description: "A localized error message") - path: [String]! @doc(description: "Path to the input field that caused an error. See the GraphQL specification about path errors for details: http://spec.graphql.org/draft/#sec-Errors") code: CartUserInputErrorType! @doc(description: "Checkout-specific error code") } diff --git a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls index 702821fd7af92..b5a12a82c84d3 100644 --- a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls +++ b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls @@ -72,7 +72,6 @@ type UpdateProductsInWishlistOutput @doc(description: "Contains the customer's w type WishListUserInputError @doc(description:"An error encountered while performing operations with WishList.") { message: String! @doc(description: "A localized error message") - path: [String]! @doc(description: "Path to the input field that caused an error. See the GraphQL specification about path errors for details: http://spec.graphql.org/draft/#sec-Errors") code: WishListUserInputErrorType! @doc(description: "Wishlist-specific error code") } From 84ceb3007de0ab169e74ed9f5be6bd8272906782 Mon Sep 17 00:00:00 2001 From: Prabhu Ram Date: Wed, 5 Aug 2020 15:44:14 -0500 Subject: [PATCH 32/37] - minor fix --- app/code/Magento/Quote/Model/Cart/AddProductsToCart.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php index 8707f7755e2fb..2b93bbd95d07b 100644 --- a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php @@ -200,7 +200,7 @@ private function addError(string $message, int $cartItemPosition = 0): void private function getErrorCode(string $message): string { foreach (self::MESSAGE_CODES as $codeMessage => $code) { - if (false !== stripos($codeMessage, $message)) { + if (false !== stripos($message, $codeMessage)) { return $code; } } From 5f8682897f3cbb9fa45d7b8a4344748514afd690 Mon Sep 17 00:00:00 2001 From: Prabhu Ram Date: Wed, 5 Aug 2020 18:10:44 -0500 Subject: [PATCH 33/37] - fix: field names should not be camel case --- .../Model/Resolver/AddProductsToCart.php | 2 +- .../Magento/QuoteGraphQl/etc/schema.graphqls | 4 ++-- .../Model/Resolver/AddProductsToWishlist.php | 2 +- .../Resolver/RemoveProductsFromWishlist.php | 2 +- .../Resolver/UpdateProductsInWishlist.php | 2 +- .../WishlistGraphQl/etc/schema.graphqls | 6 +++--- ...ddBundleProductToCartSingleMutationTest.php | 6 +++--- ...igurableProductToCartSingleMutationTest.php | 10 +++++----- ...loadableProductToCartSingleMutationTest.php | 2 +- .../AddSimpleProductToCartEndToEndTest.php | 2 +- ...ddSimpleProductToCartSingleMutationTest.php | 18 +++++++++--------- .../AddBundleProductToWishlistTest.php | 2 +- .../AddConfigurableProductToWishlistTest.php | 2 +- .../AddDownloadableProductToWishlistTest.php | 2 +- .../DeleteProductsFromWishlistTest.php | 2 +- .../UpdateProductsFromWishlistTest.php | 2 +- 16 files changed, 33 insertions(+), 33 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/AddProductsToCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/AddProductsToCart.php index a00633390eff1..d5e554f096ec1 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/AddProductsToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/AddProductsToCart.php @@ -78,7 +78,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value 'cart' => [ 'model' => $addProductsToCartOutput->getCart(), ], - 'userInputErrors' => array_map( + 'user_errors' => array_map( function (Error $error) { return [ 'code' => $error->getCode(), diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 5d11d93eedf85..4e0e7ce5732be 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -375,12 +375,12 @@ type Order { type CartUserInputError @doc(description:"An error encountered while adding an item to the the cart.") { message: String! @doc(description: "A localized error message") - code: CartUserInputErrorType! @doc(description: "Checkout-specific error code") + code: CartUserInputErrorType! @doc(description: "Cart-specific error code") } type AddProductsToCartOutput { cart: Cart! @doc(description: "The cart after products have been added") - userInputErrors:[CartUserInputError]! @doc(description: "An error encountered while adding an item to the cart.") + user_errors: [CartUserInputError!]! @doc(description: "An error encountered while adding an item to the cart.") } enum CartUserInputErrorType { diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/AddProductsToWishlist.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/AddProductsToWishlist.php index 11c8446a72a9d..3489585cd17d7 100644 --- a/app/code/Magento/WishlistGraphQl/Model/Resolver/AddProductsToWishlist.php +++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/AddProductsToWishlist.php @@ -105,7 +105,7 @@ public function resolve( return [ 'wishlist' => $this->wishlistDataMapper->map($wishlistOutput->getWishlist()), - 'userInputErrors' => array_map( + 'user_errors' => array_map( function (Error $error) { return [ 'code' => $error->getCode(), diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/RemoveProductsFromWishlist.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/RemoveProductsFromWishlist.php index 1c741361ea7b7..a59c5ccdb0f70 100644 --- a/app/code/Magento/WishlistGraphQl/Model/Resolver/RemoveProductsFromWishlist.php +++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/RemoveProductsFromWishlist.php @@ -109,7 +109,7 @@ public function resolve( return [ 'wishlist' => $this->wishlistDataMapper->map($wishlistOutput->getWishlist()), - 'userInputErrors' => \array_map( + 'user_errors' => \array_map( function (Error $error) { return [ 'code' => $error->getCode(), diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/UpdateProductsInWishlist.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/UpdateProductsInWishlist.php index 50a56863596c0..c6ede66fc2b1b 100644 --- a/app/code/Magento/WishlistGraphQl/Model/Resolver/UpdateProductsInWishlist.php +++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/UpdateProductsInWishlist.php @@ -110,7 +110,7 @@ public function resolve( return [ 'wishlist' => $this->wishlistDataMapper->map($wishlistOutput->getWishlist()), - 'userInputErrors' => \array_map( + 'user_errors' => \array_map( function (Error $error) { return [ 'code' => $error->getCode(), diff --git a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls index b5a12a82c84d3..430e77cc45e96 100644 --- a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls +++ b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls @@ -49,12 +49,12 @@ input WishlistItemInput @doc(description: "Defines the items to add to a wish li type AddProductsToWishlistOutput @doc(description: "Contains the customer's wish list and any errors encountered") { wishlist: Wishlist! @doc(description: "Contains the wish list with all items that were successfully added") - userInputErrors:[WishListUserInputError]! @doc(description: "An array of errors encountered while adding products to a wish list") + user_errors:[WishListUserInputError!]! @doc(description: "An array of errors encountered while adding products to a wish list") } type RemoveProductsFromWishlistOutput @doc(description: "Contains the customer's wish list and any errors encountered") { wishlist: Wishlist! @doc(description: "Contains the wish list with after items were successfully deleted") - userInputErrors:[WishListUserInputError]! @doc(description:"An array of errors encountered while deleting products from a wish list") + user_errors:[WishListUserInputError!]! @doc(description:"An array of errors encountered while deleting products from a wish list") } input WishlistItemUpdateInput @doc(description: "Defines updates to items in a wish list") { @@ -67,7 +67,7 @@ input WishlistItemUpdateInput @doc(description: "Defines updates to items in a w type UpdateProductsInWishlistOutput @doc(description: "Contains the customer's wish list and any errors encountered") { wishlist: Wishlist! @doc(description: "Contains the wish list with all items that were successfully updated") - userInputErrors:[WishListUserInputError]! @doc(description:"An array of errors encountered while updating products in a wish list") + user_errors: [WishListUserInputError!]! @doc(description:"An array of errors encountered while updating products in a wish list") } type WishListUserInputError @doc(description:"An error encountered while performing operations with WishList.") { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php index be1bfce9441d6..fc0fdcf71525f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php @@ -208,7 +208,7 @@ public function testAddBundleToCartWithWrongBundleOptions() } } } - userInputErrors { + user_errors { message } } @@ -219,7 +219,7 @@ public function testAddBundleToCartWithWrongBundleOptions() self::assertEquals( "Please select all required options.", - $response['addProductsToCart']['userInputErrors'][0]['message'] + $response['addProductsToCart']['user_errors'][0]['message'] ); } @@ -341,7 +341,7 @@ private function getMutationsQuery( } } } - userInputErrors { + user_errors { message } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php index 3612bc7f26e49..a2b7b54fb875a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartSingleMutationTest.php @@ -103,7 +103,7 @@ public function testAddConfigurableProductWithWrongSuperAttributes() self::assertEquals( 'You need to choose options for your item.', - $response['addProductsToCart']['userInputErrors'][0]['message'] + $response['addProductsToCart']['user_errors'][0]['message'] ); } @@ -132,7 +132,7 @@ public function testAddProductIfQuantityIsNotAvailable() self::assertEquals( 'The requested qty is not available', - $response['addProductsToCart']['userInputErrors'][0]['message'] + $response['addProductsToCart']['user_errors'][0]['message'] ); } @@ -156,7 +156,7 @@ public function testAddNonExistentConfigurableProductParentToCart() self::assertEquals( 'Could not find a product with SKU "configurable_no_exist"', - $response['addProductsToCart']['userInputErrors'][0]['message'] + $response['addProductsToCart']['user_errors'][0]['message'] ); } @@ -188,7 +188,7 @@ public function testOutOfStockVariationToCart() 'This product is out of stock.' ]; $this->assertContains( - $response['addProductsToCart']['userInputErrors'][0]['message'], + $response['addProductsToCart']['user_errors'][0]['message'], $expectedErrorMessages ); } @@ -235,7 +235,7 @@ private function getQuery( } } }, - userInputErrors { + user_errors { message } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddDownloadableProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddDownloadableProductToCartSingleMutationTest.php index ce613d2d958c0..956316c1fa0fa 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddDownloadableProductToCartSingleMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddDownloadableProductToCartSingleMutationTest.php @@ -186,7 +186,7 @@ private function getQuery( } } }, - userInputErrors { + user_errors { message } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartEndToEndTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartEndToEndTest.php index f416dad46f64a..d8f7aedfdd583 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartEndToEndTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartEndToEndTest.php @@ -250,7 +250,7 @@ private function getAddToCartMutation( } } }, - userInputErrors { + user_errors { message } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php index 139aa38bc24c0..4e50f6ff3a2ca 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartSingleMutationTest.php @@ -112,11 +112,11 @@ public function testAddProductWithWrongSku(string $sku, string $message) $query = $this->getAddToCartMutation($maskedQuoteId, 1, $sku, ''); $response = $this->graphQlMutation($query); - self::assertArrayHasKey('userInputErrors', $response['addProductsToCart']); - self::assertCount(1, $response['addProductsToCart']['userInputErrors']); + self::assertArrayHasKey('user_errors', $response['addProductsToCart']); + self::assertCount(1, $response['addProductsToCart']['user_errors']); self::assertEquals( $message, - $response['addProductsToCart']['userInputErrors'][0]['message'] + $response['addProductsToCart']['user_errors'][0]['message'] ); } @@ -140,10 +140,10 @@ public function testAddToCartWithQtyPlusOne() $query = $this->getAddToCartMutation($maskedQuoteId, 1, $sku, ''); $response = $this->graphQlMutation($query); - self::assertArrayHasKey('userInputErrors', $response['addProductsToCart']); + self::assertArrayHasKey('user_errors', $response['addProductsToCart']); self::assertEquals( 'The requested qty is not available', - $response['addProductsToCart']['userInputErrors'][0]['message'] + $response['addProductsToCart']['user_errors'][0]['message'] ); self::assertEquals(100, $response['addProductsToCart']['cart']['total_quantity']); } @@ -164,12 +164,12 @@ public function testAddProductWithWrongQuantity(int $quantity, string $message) $query = $this->getAddToCartMutation($maskedQuoteId, $quantity, $sku, ''); $response = $this->graphQlMutation($query); - self::assertArrayHasKey('userInputErrors', $response['addProductsToCart']); - self::assertCount(1, $response['addProductsToCart']['userInputErrors']); + self::assertArrayHasKey('user_errors', $response['addProductsToCart']); + self::assertCount(1, $response['addProductsToCart']['user_errors']); self::assertEquals( $message, - $response['addProductsToCart']['userInputErrors'][0]['message'] + $response['addProductsToCart']['user_errors'][0]['message'] ); } @@ -249,7 +249,7 @@ private function getAddToCartMutation( } } }, - userInputErrors { + user_errors { message } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddBundleProductToWishlistTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddBundleProductToWishlistTest.php index c0199e8908d0e..a81ec701b22a8 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddBundleProductToWishlistTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddBundleProductToWishlistTest.php @@ -140,7 +140,7 @@ private function getQuery( } ] ) { - userInputErrors { + user_errors { code message } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddConfigurableProductToWishlistTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddConfigurableProductToWishlistTest.php index 386df99f0d211..d8d44541f899d 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddConfigurableProductToWishlistTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddConfigurableProductToWishlistTest.php @@ -126,7 +126,7 @@ private function getQuery( } ] ) { - userInputErrors { + user_errors { code message } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddDownloadableProductToWishlistTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddDownloadableProductToWishlistTest.php index 389f4eae4c574..489a960056f1b 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddDownloadableProductToWishlistTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddDownloadableProductToWishlistTest.php @@ -181,7 +181,7 @@ private function getQuery( } ] ) { - userInputErrors { + user_errors { code message } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/DeleteProductsFromWishlistTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/DeleteProductsFromWishlistTest.php index 2e203e3ff4228..ebe99289b8934 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/DeleteProductsFromWishlistTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/DeleteProductsFromWishlistTest.php @@ -90,7 +90,7 @@ private function getQuery( wishlistId: {$wishlistId}, wishlistItemsIds: [{$wishlistItemId}] ) { - userInputErrors { + user_errors { code message } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/UpdateProductsFromWishlistTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/UpdateProductsFromWishlistTest.php index 9e96bdc5d7079..9a9cd424e54ca 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/UpdateProductsFromWishlistTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/UpdateProductsFromWishlistTest.php @@ -102,7 +102,7 @@ private function getQuery( } ] ) { - userInputErrors { + user_errors { code message } From 762a3c15c0ee9bef6d805dd114e7d08f9a35ba57 Mon Sep 17 00:00:00 2001 From: Prabhu Ram Date: Wed, 5 Aug 2020 18:15:23 -0500 Subject: [PATCH 34/37] - reverting .htaccess deletion --- vendor/.htaccess | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 vendor/.htaccess diff --git a/vendor/.htaccess b/vendor/.htaccess new file mode 100644 index 0000000000000..b97408bad3f2e --- /dev/null +++ b/vendor/.htaccess @@ -0,0 +1,7 @@ + + order allow,deny + deny from all + += 2.4> + Require all denied + From 473b8d81f9c865080fe41cdb851b3530fbd5925b Mon Sep 17 00:00:00 2001 From: Prabhu Ram Date: Thu, 6 Aug 2020 11:08:17 -0500 Subject: [PATCH 35/37] - Fixed bug to save a product if another product in cart has an error --- app/code/Magento/Quote/Model/Cart/AddProductsToCart.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php index 2b93bbd95d07b..3f6e13af2eae7 100644 --- a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php @@ -115,9 +115,7 @@ public function execute(string $maskedCartId, array $cartItems): AddProductsToCa } } - if (count($this->errors) === 0) { - $this->cartRepository->save($cart); - } else { + if (!count($this->errors) === 0) { /* Revert changes introduced by add to cart processes in case of an error */ $cart->getItemsCollection()->clear(); } @@ -155,6 +153,7 @@ private function addItemToCart(CartInterface $cart, Data\CartItem $cartItem, int try { $result = $cart->addProduct($product, $this->requestBuilder->build($cartItem)); + $this->cartRepository->save($cart); } catch (\Throwable $e) { $this->addError( __($e->getMessage())->render(), From 439123520349caf39173fa074a52ac686bbeffd7 Mon Sep 17 00:00:00 2001 From: Prabhu Ram Date: Thu, 6 Aug 2020 15:04:15 -0500 Subject: [PATCH 36/37] - minor fix --- app/code/Magento/Quote/Model/Cart/AddProductsToCart.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php index 3f6e13af2eae7..2c5c3536d6682 100644 --- a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php @@ -115,7 +115,7 @@ public function execute(string $maskedCartId, array $cartItems): AddProductsToCa } } - if (!count($this->errors) === 0) { + if (count($this->errors) !== 0) { /* Revert changes introduced by add to cart processes in case of an error */ $cart->getItemsCollection()->clear(); } From a556345e619e79c123b881090e893d33f67dae61 Mon Sep 17 00:00:00 2001 From: Prabhu Ram Date: Mon, 10 Aug 2020 19:25:53 -0500 Subject: [PATCH 37/37] minor fix --- .../Resolver/Product/CustomizableEnteredOptionValueUid.php | 3 +++ .../Resolver/Product/CustomizableSelectedOptionValueUid.php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CustomizableEnteredOptionValueUid.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CustomizableEnteredOptionValueUid.php index 69fafc49c9137..2d6975bb8a4e2 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CustomizableEnteredOptionValueUid.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CustomizableEnteredOptionValueUid.php @@ -45,6 +45,9 @@ public function resolve( array $value = null, array $args = null ) { + if (isset($value['uid'])) { + return $value['uid']; + } if (!isset($value['option_id']) || empty($value['option_id'])) { throw new GraphQlInputException(__('"option_id" value should be specified.')); } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CustomizableSelectedOptionValueUid.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CustomizableSelectedOptionValueUid.php index 5fbd8a56bb570..795782d6e3718 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CustomizableSelectedOptionValueUid.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CustomizableSelectedOptionValueUid.php @@ -45,6 +45,9 @@ public function resolve( array $value = null, array $args = null ) { + if (isset($value['uid'])) { + return $value['uid']; + } if (!isset($value['option_id']) || empty($value['option_id'])) { throw new GraphQlInputException(__('"option_id" value should be specified.')); }