Skip to content

Commit

Permalink
Merge pull request #88 from BitBagCommerce/release/20230629
Browse files Browse the repository at this point in the history
Release 20230629
  • Loading branch information
senghe authored Jun 29, 2023
2 parents 8bfa0b3 + 11d445e commit fe5f3df
Show file tree
Hide file tree
Showing 20 changed files with 345 additions and 0 deletions.
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
"ext-openssl": "*",
"bitbag/wishlist-plugin": "^3.0",
"gesdinet/jwt-refresh-token-bundle": "^0.12.0",
"php-http/message-factory": "^1.1",
"sylius/sylius": "~1.11.0",
"symfony/mailer": "^6.0",
"webonyx/graphql-php": "^14.9"
},
"require-dev": {
Expand Down
37 changes: 37 additions & 0 deletions features/shop/account/resetting_password.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@resetting_password
Feature: Resetting one customer password
In order to reset my password
As a Customer
I need to be able to reset it by reset query


Background:
Given the store operates on a single channel in "United States"
And the store has locale "en_US"
And there is a customer account "[email protected]"

@graphql
Scenario: Setting token for password reset
Given I prepare password reset request operation for user "[email protected]"
And I send that GraphQL request
Then I should receive a JSON response
And This response should contain pattern '/^\{"data":\{"shop_send_reset_password_emailCustomer":\{"customer":\{"id":"\/api\/v2\/shop\/customers\/(\d+)"\}\}\}\}$/'
And user "[email protected]" should have reset password token set

@graphql
Scenario: Checking for valid reset password token availability
Given I prepare password reset request operation for user "[email protected]"
And I send that GraphQL request
And I prepare check reset password token operation for user's "[email protected]" token
And I send that GraphQL request
Then I should receive a JSON response
And This response should contain pattern '/^\{"data":\{"password_reset_tokenUser":\{"username":"([^"]+)"\}\}\}$/'

@graphql
Scenario: Checking for invalid reset password token availability
Given I prepare password reset request operation for user "[email protected]"
And I send that GraphQL request
And I prepare check reset password token operation for invalid token
And I send that GraphQL request
Then I should receive a JSON response
And This response should contain pattern '/"data":\{"password_reset_tokenUser":null\}/'
33 changes: 33 additions & 0 deletions migrations/Version20230612094617.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/*
* This file has been created by developers from BitBag.
* Feel free to contact us once you face any issues or want to start
* You can find more information about us on https://bitbag.io and write us
* an email on [email protected].
*/

declare(strict_types=1);

namespace App\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20230612094617 extends AbstractMigration
{
public function getDescription(): string
{
return 'Create refresh token table';
}

public function up(Schema $schema): void
{
$this->addSql('CREATE TABLE IF NOT EXISTS bitbag_refresh_token (id INT AUTO_INCREMENT NOT NULL, refresh_token VARCHAR(128) NOT NULL, username VARCHAR(255) NOT NULL, valid DATETIME NOT NULL, remember_me TINYINT(1) NOT NULL, UNIQUE INDEX UNIQ_CD7BD0E9C74F2195 (refresh_token), PRIMARY KEY(id)) DEFAULT CHARACTER SET UTF8 COLLATE `UTF8_unicode_ci` ENGINE = InnoDB');
}

public function down(Schema $schema): void
{
$this->addSql('DROP TABLE bitbag_refresh_token');
}
}
33 changes: 33 additions & 0 deletions migrations/Version20230612095046.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/*
* This file has been created by developers from BitBag.
* Feel free to contact us once you face any issues or want to start
* You can find more information about us on https://bitbag.io and write us
* an email on [email protected].
*/

declare(strict_types=1);

namespace App\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20230612095046 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add index to sylius_product_attribute_value table on `locale_code`';
}

public function up(Schema $schema): void
{
$this->addSql('CREATE INDEX locale_code ON sylius_product_attribute_value (locale_code)');
}

public function down(Schema $schema): void
{
$this->addSql('DROP INDEX locale_code ON sylius_product_attribute_value');
}
}
1 change: 1 addition & 0 deletions spec/CommandHandler/Account/ResetPasswordHandlerSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ public function it_is_invokable(

$user->setPlainPassword($command->newPassword)->shouldBeCalled();
$passwordUpdater->updatePassword($user->getWrappedObject())->shouldBeCalled();
$user->setPasswordResetToken(null)->shouldBeCalled();

$user->getCustomer()->willReturn($customer);

Expand Down
68 changes: 68 additions & 0 deletions spec/Resolver/Query/PasswordResetTokenResolverSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

/*
* This file has been created by developers from BitBag.
* Feel free to contact us once you face any issues or want to start
* You can find more information about us on https://bitbag.io and write us
* an email on [email protected].
*/

declare(strict_types=1);


namespace spec\BitBag\SyliusVueStorefront2Plugin\Resolver\Query;

use BitBag\SyliusVueStorefront2Plugin\Resolver\Query\PasswordResetTokenResolver;
use PhpSpec\ObjectBehavior;
use Sylius\Component\Core\Model\ShopUserInterface;
use Sylius\Component\User\Repository\UserRepositoryInterface;

class PasswordResetTokenResolverSpec extends ObjectBehavior
{
public function let(
UserRepositoryInterface $userRepository
): void {
$this->beConstructedWith(
$userRepository
);
}

public function it_is_initializable(): void
{
$this->shouldHaveType(PasswordResetTokenResolver::class);
}

public function it_returns_user_on_valid_token(
UserRepositoryInterface $userRepository,
ShopUserInterface $user
): void {
$context = [
'args' => [
'passwordResetToken' => "TOKEN",
],
];

$token = $context['args']['passwordResetToken'];

$userRepository->findOneBy(['passwordResetToken' => $token])->willReturn($user);

$this->__invoke(null, $context)->shouldReturn($user);
}

public function it_should_return_null_on_invalid_token(
UserRepositoryInterface $userRepository,
): void {
$context = [
'args' => [
'passwordResetToken' => "TOKEN",
],
];

$token = $context['args']['passwordResetToken'];

$userRepository->findOneBy(['passwordResetToken' => $token])->willReturn(null);

$this->shouldThrow(\RuntimeException::class)
->during('__invoke', [null, $context]);
}
}
1 change: 1 addition & 0 deletions src/CommandHandler/Account/ResetPasswordHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public function __invoke(ResetPassword $command): CustomerInterface
$user->setPlainPassword($command->newPassword);

$this->passwordUpdater->updatePassword($user);
$user->setPasswordResetToken(null);

$customer = $user->getCustomer();
Assert::notNull($customer);
Expand Down
39 changes: 39 additions & 0 deletions src/Resolver/Query/PasswordResetTokenResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

/*
* This file has been created by developers from BitBag.
* Feel free to contact us once you face any issues or want to start
* You can find more information about us on https://bitbag.io and write us
* an email on [email protected].
*/

declare(strict_types=1);

namespace BitBag\SyliusVueStorefront2Plugin\Resolver\Query;

use ApiPlatform\GraphQl\Resolver\QueryItemResolverInterface;
use Sylius\Component\Core\Model\ShopUserInterface;
use Sylius\Component\User\Repository\UserRepositoryInterface;

class PasswordResetTokenResolver implements QueryItemResolverInterface
{
private UserRepositoryInterface $userRepository;

public function __construct(UserRepositoryInterface $userRepository)
{
$this->userRepository = $userRepository;
}

public function __invoke($item, array $context): ShopUserInterface
{
$token = $context['args']['passwordResetToken'];
/** @var ?ShopUserInterface $user */
$user = $this->userRepository->findOneBy(['passwordResetToken' => $token]);

if (null !== $user) {
return $user;
}

throw new \RuntimeException('Token not found');
}
}
12 changes: 12 additions & 0 deletions src/Resources/api_resources/ShopUser.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ We are hiring developers from all over the world. Join us and start your new, ex
</operation>

<operation name="item_query"/>

<operation name="password_reset_token">
<attribute name="normalization_context">
<attribute name="groups">shop:password_reset_token:read</attribute>
</attribute>
<attribute name="item_query">bitbag.sylius_vue_storefront2plugin.resolver.query.password_reset_token_resolver</attribute>
<attribute name="args">
<attribute name="passwordResetToken">
<attribute name="type">String!</attribute>
</attribute>
</attribute>
</operation>
</graphql>

<property name="id" identifier="true" writable="false" />
Expand Down
3 changes: 3 additions & 0 deletions src/Resources/serialization/ShopUser.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,8 @@ We are hiring developers from all over the world. Join us and start your new, ex
<group>shop:customer:read</group>
<group>shop:user_login:read</group>
</attribute>
<attribute name="username">
<group>shop:password_reset_token:read</group>
</attribute>
</class>
</serializer>
1 change: 1 addition & 0 deletions src/Resources/services/behat/contexts.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<argument type="service" id="sylius.repository.shop_user" />
<argument type="service" id="bitbag.sylius_vue_storefront2_plugin.factory.shop_user_token_factory" />
<argument type="service" id="api_platform.iri_converter" />
<argument type="service" id="doctrine.orm.entity_manager" />
</service>

<service id="bitbag.sylius_vue_storefront2_plugin.context.login"
Expand Down
6 changes: 6 additions & 0 deletions src/Resources/services/resolvers.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ We are hiring developers from all over the world. Join us and start your new, ex
<tag name="api_platform.graphql.mutation_resolver" />
</service>

<service id="bitbag.sylius_vue_storefront2plugin.resolver.query.password_reset_token_resolver"
class="BitBag\SyliusVueStorefront2Plugin\Resolver\Query\PasswordResetTokenResolver">
<argument type="service" id="sylius.repository.shop_user" />
<tag name="api_platform.graphql.query_resolver" />
</service>

<service id="bitbag.sylius_vue_storefront2_plugin.resolver.order_address_state_resolver"
class="BitBag\SyliusVueStorefront2Plugin\Resolver\OrderAddressStateResolver">
<argument type="service" id="sm.factory" />
Expand Down
7 changes: 7 additions & 0 deletions tests/Application/.env
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ JWT_PASSPHRASE=acme_plugin_development
MAILER_URL=smtp://localhost
###< symfony/swiftmailer-bundle ###

###> symfony/mailer ###
# For Gmail as a transport, use: "gmail://username:password@localhost"
# For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode="
# Delivery is disabled by default via "null://null"
MAILER_DSN=null://null
###< symfony/mailer ###

###> symfony/messenger ###
# Choose one of the transports below
# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages
Expand Down
3 changes: 3 additions & 0 deletions tests/Application/config/packages/_sylius.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@ sylius_attribute:

sylius_api:
enabled: true

sylius_mailer:
sender_adapter: sylius.email_sender.adapter.symfony_mailer
3 changes: 3 additions & 0 deletions tests/Application/config/packages/doctrine_migrations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ doctrine_migrations:
storage:
table_storage:
table_name: sylius_migrations

migrations_paths:
'migrations': '%kernel.project_dir%/../../migrations'
2 changes: 2 additions & 0 deletions tests/Application/config/packages/framework.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ framework:
mapping:
paths:
- '%kernel.project_dir%/../../src/Resources/serialization'
mailer:
dsn: '%env(MAILER_DSN)%'
9 changes: 9 additions & 0 deletions tests/Behat/Context/GraphqlApiPlatformContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -303,4 +303,13 @@ public function iAddFilterToThisOperation(string $filterName, $value, string $ty
$value = $this->castToType($value, $type);
$operation->addFilter($filterName, $value);
}

/**
* @Then This response should contain pattern :pattern
*/
public function responseShouldContainPattern(string $pattern): void{
$response = $this->client->getLastResponse();
$data = stripslashes($response->getContent());
Assert::true((bool)preg_match($pattern, $data));
}
}
Loading

0 comments on commit fe5f3df

Please sign in to comment.