Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding an Address book endpoin in the API #285

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions LoggedInCustomerDetailsActionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

declare(strict_types=1);

namespace Tests\Sylius\ShopApiPlugin\Controller\Customer;

use Sylius\Component\Channel\Context\ChannelContextInterface;
use Tests\Sylius\ShopApiPlugin\Controller\TestCase\JsonApiTestCase;
use Symfony\Component\HttpFoundation\Response;

final class LoggedInCustomerDetailsActionTest extends JsonApiTestCase
{
/**
* @test
*/
public function it_shows_currently_logged_in_customer_details()
{
$this->loadFixturesFromFile('customer.yml');
$this->loadFixturesFromFile('channel.yml');

$fakeChannelContext = $this->createMock(ChannelContextInterface::class);
$fakeChannelContext->method('getChannel')->willReturn($this->get('sylius.repository.channel')->findOneByCode('WEB_GB'));
$this->client->getContainer()->set('sylius.context.channel', $fakeChannelContext);

$data =
<<<EOT
{
"_username": "oliver@queen.com",
"_password": "123pa\$\$word"
}
EOT;

$this->client->request('POST', '/shop-api/login_check', [], [], ['CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'], $data);

$response = json_decode($this->client->getResponse()->getContent(), true);
$this->client->setServerParameter('HTTP_Authorization', sprintf('Bearer %s', $response['token']));

$this->client->request('GET', '/shop-api/me', [], [], [
'CONTENT_TYPE' => 'application/json',
'ACCEPT' => 'application/json',
]);

$response = $this->client->getResponse();
$this->assertResponse($response, 'customer/logged_in_customer_details_response', Response::HTTP_OK);
}

/**
* @test
*/
public function it_shows_currently_logged_in_customer_addresses()
{
$this->loadFixturesFromFile('customer.yml');
$this->loadFixturesFromFile('channel.yml');

$fakeChannelContext = $this->createMock(ChannelContextInterface::class);
$fakeChannelContext->method('getChannel')->willReturn($this->get('sylius.repository.channel')->findOneByCode('WEB_GB'));
$this->client->getContainer()->set('sylius.context.channel', $fakeChannelContext);

$data =
<<<EOT
{
"_username": "oliver@queen.com",
"_password": "123pa\$\$word"
}
EOT;

$this->client->request('POST', '/shop-api/login_check', [], [], ['CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'], $data);

$response = json_decode($this->client->getResponse()->getContent(), true);
$this->client->setServerParameter('HTTP_Authorization', sprintf('Bearer %s', $response['token']));

$this->client->request('GET', '/shop-api/me/address', [], [], [
'CONTENT_TYPE' => 'application/json',
'ACCEPT' => 'application/json',
]);

$response = $this->client->getResponse();
$this->assertResponse($response, 'customer/logged_in_customer_addresses', Response::HTTP_OK);
}
}
71 changes: 71 additions & 0 deletions doc/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -829,11 +829,49 @@ paths:
operationId: "me"
responses:
200:
description: "User was logged in"
schema:
type: "object"
$ref: "#/definitions/LoggedInCustomerDetails"
500:
description: "There is no currently logged in user."
put:
tags:
- "users"
summary: "Updates currently logged in users details."
operationId: "me"
consumes:
- "application/json"
parameters:
- name: "content"
in: "body"
required: true
schema:
$ref: "#/definitions/UpdateUserRequest"
responses:
200:
description: "User successfully updated."
schema:
$ref: "#/definitions/LoggedInCustomerDetails"
401:
description: "User token is invalid."
500:
description: "There is no currently logged in user."
/me/address:
get:
tags:
- "users"
summary: "Gets the address book of the user"
operationId: "userAddressSummary"
responses:
200:
description: "A user is logged in."
schema:
$ref: "#/definitions/LoggedInCustomerAddress"
401:
description: "User token is invalid."
500:
description: "There is no currently logged in user."

definitions:
PickupCartRequest:
Expand Down Expand Up @@ -1402,6 +1440,30 @@ definitions:
email:
type: "string"
example: "test@example.com"
UpdateUserRequest:
type: object
properties:
firstName:
type: string
example: 'Sherlock'
lastName:
type: string
example: 'Holmes'
email:
type: string
example: 'sherlock@holmes.com'
birthday:
type: string
example: '2017-08-12'
gender:
type: string
example: 'f'
phoneNumber:
type: string
example: "+490000000000"
subscribedToNewsletter:
type: integer
example: 0
LoggedInCustomerDetails:
type: "object"
properties:
Expand All @@ -1414,6 +1476,15 @@ definitions:
email:
type: "string"
example: "sherlock@holmes.com"
LoggedInCustomerAddress:
type: "object"
properties:
defaultAddress:
$ref: "#/definitions/Address"
otherAddresses:
type: array
items:
$ref: "#/definitions/Address"
GeneralError:
type: "object"
properties:
Expand Down
84 changes: 84 additions & 0 deletions spec/Factory/AddressBookViewFactorySpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

declare(strict_types=1);

namespace spec\Sylius\ShopApiPlugin\Factory;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use PhpSpec\ObjectBehavior;
use Sylius\Component\Core\Model\AddressInterface;
use Sylius\ShopApiPlugin\Factory\AddressBookViewFactory;
use Sylius\ShopApiPlugin\Factory\AddressBookViewFactoryInterface;
use Sylius\ShopApiPlugin\View\AddressBookView;
use Sylius\ShopApiPlugin\View\AddressView;

final class AddressBookViewFactorySpec extends ObjectBehavior
{
function let(AddressBookViewFactory $addressViewFactory)
{
$this->beConstructedWith($addressViewFactory);
}

function it_is_address_book_view_factory()
{
$this->shouldHaveType(AddressBookViewFactoryInterface::class);
}

function it_creates_address_book_view_with_default_address(
AddressInterface $address
) {
$address->getFirstName()->willReturn('Sherlock');
$address->getLastName()->willReturn('Holmes');
$address->getStreet()->willReturn('Baker Street 221b');
$address->getCountryCode()->willReturn('GB');
$address->getCity()->willReturn('London');
$address->getPostcode()->willReturn('NMW');
$address->getProvinceName()->willReturn('Greater London');

$addressView = new AddressView();
$addressView->firstName = 'Sherlock';
$addressView->lastName = 'Holmes';
$addressView->street = 'Baker Street 221b';
$addressView->countryCode = 'GB';
$addressView->city = 'London';
$addressView->postcode = 'NMW';
$addressView->provinceName = 'Greater London';

$addressBookView = new AddressBookView();
$addressBookView->defaultAddress = $addressView;
$addressBookView->addresses = [];

$this->create($address, new ArrayCollection())->shouldBeLike($addressBookView);
}

function it_creates_address_book_view_with_default_address_and_otherAddresses(
AddressInterface $address
) {
$address->getFirstName()->willReturn('Sherlock');
$address->getLastName()->willReturn('Holmes');
$address->getStreet()->willReturn('Baker Street 221b');
$address->getCountryCode()->willReturn('GB');
$address->getCity()->willReturn('London');
$address->getPostcode()->willReturn('NMW');
$address->getProvinceName()->willReturn('Greater London');

$addressView = new AddressView();
$addressView->firstName = 'Sherlock';
$addressView->lastName = 'Holmes';
$addressView->street = 'Baker Street 221b';
$addressView->countryCode = 'GB';
$addressView->city = 'London';
$addressView->postcode = 'NMW';
$addressView->provinceName = 'Greater London';

$addressBookView = new AddressBookView();
$addressBookView->defaultAddress = $addressView;
$addressBookView->addresses = [$addressBookView];

$this
->create($address, new ArrayCollection([$address]))
->shouldBeLike($addressBookView)
;
}
}
2 changes: 1 addition & 1 deletion spec/Factory/AddressViewFactorySpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function let()
$this->beConstructedWith(AddressView::class);
}

function it_is_image_view_builder()
function it_is_address_view_factory()
{
$this->shouldHaveType(AddressViewFactoryInterface::class);
}
Expand Down
68 changes: 68 additions & 0 deletions src/Controller/Customer/CustomerAddressAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);

namespace Sylius\ShopApiPlugin\Controller\Customer;

use FOS\RestBundle\View\View;
use FOS\RestBundle\View\ViewHandlerInterface;
use Sylius\Component\Core\Model\AddressInterface;
use Sylius\Component\Core\Model\Customer;
use Sylius\Component\Core\Model\ShopUserInterface;
use Sylius\ShopApiPlugin\Factory\AddressBookViewFactoryInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Webmozart\Assert\Assert;

final class CustomerAddressAction
{
/**
* @var ViewHandlerInterface
*/
private $viewHandler;

/**
* @var TokenStorageInterface
*/
private $tokenStorage;

/**
* @var AddressBookViewFactoryInterface
*/
private $addressBookViewFactory;

/**
* @param ViewHandlerInterface $viewHandler
* @param TokenStorageInterface $tokenStorage
* @param AddressBookViewFactoryInterface $addressBookViewFactory
*/
public function __construct(
ViewHandlerInterface $viewHandler,
TokenStorageInterface $tokenStorage,
AddressBookViewFactoryInterface $addressBookViewFactory
) {
$this->viewHandler = $viewHandler;
$this->tokenStorage = $tokenStorage;
$this->addressBookViewFactory = $addressBookViewFactory;
}

public function __invoke(Request $request): Response
{
/** @var ShopUserInterface $user */
$user = $this->tokenStorage->getToken()->getUser();

Assert::isInstanceOf($user, ShopUserInterface::class);
$customer = $user->getCustomer();

/** @var Customer $customer */
Assert::isInstanceOf($customer, Customer::class);

return $this->viewHandler->handle(
View::create($this->addressBookViewFactory->create(
$customer->getDefaultAddress(),
$customer->getAddresses()
),Response::HTTP_OK)
);
}

}
41 changes: 41 additions & 0 deletions src/Factory/AddressBookViewFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);

namespace Sylius\ShopApiPlugin\Factory;

use Doctrine\Common\Collections\Collection;
use Sylius\Component\Core\Model\AddressInterface;
use Sylius\ShopApiPlugin\View\AddressBookView;
use Sylius\ShopApiPlugin\View\AddressView;

final class AddressBookViewFactory implements AddressBookViewFactoryInterface
{
/**
* @var AddressViewFactoryInterface
*/
private $addressViewFactory;

/**
* AddressBookViewFactory constructor.
*
* @param AddressViewFactoryInterface $addressViewFactory
*/
public function __construct(
AddressViewFactoryInterface $addressViewFactory
) {
$this->addressViewFactory = $addressViewFactory;
}

public function create(?AddressInterface $address, Collection $otherAddress): AddressBookView
{
/** @var AddressBookView $addressBookView */
$addressBookView = new AddressBookView();
$addressBookView->defaultAddress = ($address === null) ? null : $this->addressViewFactory->create($address);
$addressBookView->addresses = $otherAddress->map(
function (AddressInterface $address): AddressView {
return $this->addressViewFactory->create($address);
}
);
return $addressBookView;
}
}
Loading