Skip to content

Commit

Permalink
Merge 1fa4158 into bd7a0fb
Browse files Browse the repository at this point in the history
  • Loading branch information
OndraM committed Nov 27, 2017
2 parents bd7a0fb + 1fa4158 commit ee6eaf5
Show file tree
Hide file tree
Showing 8 changed files with 238 additions and 10 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -8,6 +8,8 @@
### Added
- `UserMerge` command and `addUserMerge` & `addUserMerges` method for `EventsRequestBuilder`. (Accessible via `$matej->events()->...`).
- `Interaction` command and `addInteraction` & `addInteractions` method for `EventsRequestBuilder`. (Accessible via `$matej->events()->...`).
- `CampaignRequestBuilder` to request batch of recommendations and item sortings for multiple users. (Accessible via `$matej->campaign()->...`).
- Method `isSuccessfull()` of `CommandResponse` for easy and encapsulated detection of successful command responses.

### Fixed
- Return types of methods in `RequestBuilderFactory` were not defined (and were not guessable by an IDE) for PHP 5 version.
Expand Down
19 changes: 19 additions & 0 deletions README.md
Expand Up @@ -80,6 +80,25 @@ $response = $matej->request()
->send();
```

### Request batch of recommendations/item sortings

Use `campaign()` builder to request batch of recommendations or item sorting for multiple users.
Typical use case for this is generating emailing campaigns.

```php
$matej = new Matej('accountId', 'apikey');

$response = $matej->request()
->campaign()
// Request item sortings
->addSorting(Sorting::create('user-id', ['item-id-1', 'item-id-2', 'item-id-3']))
->addSortings([/* array of Sorting objects */])
// Request user-based recommendations
->addRecommendation(UserRecommendation::create('user-id', 10, 'emailing', 1.0, 3600))
->addRecommendations([/* array of UserRecommendation objects */])
->send();
```

## Changelog
For latest changes see [CHANGELOG.md](CHANGELOG.md) file. We follow [Semantic Versioning](http://semver.org/).

Expand Down
5 changes: 5 additions & 0 deletions src/Model/CommandResponse.php
Expand Up @@ -53,4 +53,9 @@ public function getData(): array
{
return $this->data;
}

public function isSuccessful(): bool
{
return $this->getStatus() === self::STATUS_OK;
}
}
67 changes: 67 additions & 0 deletions src/RequestBuilder/CampaignRequestBuilder.php
@@ -0,0 +1,67 @@
<?php declare(strict_types=1);

namespace Lmc\Matej\RequestBuilder;

use Fig\Http\Message\RequestMethodInterface;
use Lmc\Matej\Exception\LogicException;
use Lmc\Matej\Model\Command\AbstractCommand;
use Lmc\Matej\Model\Command\Sorting;
use Lmc\Matej\Model\Command\UserRecommendation;
use Lmc\Matej\Model\Request;

class CampaignRequestBuilder extends AbstractRequestBuilder
{
protected const ENDPOINT_PATH = '/campaign';

/** @var AbstractCommand[] */
protected $commands = [];

public function addRecommendation(UserRecommendation $recommendation): self
{
$this->commands[] = $recommendation;

return $this;
}

/**
* @param UserRecommendation[] $recommendations
* @return self
*/
public function addRecommendations(array $recommendations): self
{
foreach ($recommendations as $recommendation) {
$this->addRecommendation($recommendation);
}

return $this;
}

public function addSorting(Sorting $sorting): self
{
$this->commands[] = $sorting;

return $this;
}

/**
* @param Sorting[] $sortings
* @return self
*/
public function addSortings(array $sortings): self
{
foreach ($sortings as $sorting) {
$this->addSorting($sorting);
}

return $this;
}

public function build(): Request
{
if (empty($this->commands)) {
throw new LogicException('At least one command must be added to the builder before sending the request');
}

return new Request(self::ENDPOINT_PATH, RequestMethodInterface::METHOD_POST, $this->commands);
}
}
8 changes: 8 additions & 0 deletions src/RequestBuilder/RequestBuilderFactory.php
Expand Up @@ -46,6 +46,14 @@ public function events(): EventsRequestBuilder
return $this->createConfiguredBuilder(EventsRequestBuilder::class);
}

/**
* @return CampaignRequestBuilder
*/
public function campaign(): CampaignRequestBuilder
{
return $this->createConfiguredBuilder(CampaignRequestBuilder::class);
}

// TODO: builders for other endpoints

/**
Expand Down
48 changes: 38 additions & 10 deletions tests/Model/CommandResponseTest.php
Expand Up @@ -32,32 +32,60 @@ public function provideObjectResponses(): array
{
return [
'OK response with only status' => [
(object) ['status' => 'OK'],
'OK',
(object) ['status' => CommandResponse::STATUS_OK],
CommandResponse::STATUS_OK,
'',
[],
],
'OK response with status and empty message and data' => [
(object) ['status' => 'OK', 'message' => '', 'data' => []],
'OK',
(object) ['status' => CommandResponse::STATUS_OK, 'message' => '', 'data' => []],
CommandResponse::STATUS_OK,
'',
[],
],
'OK response with all fields' => [
(object) ['status' => 'OK', 'message' => 'Nice!', 'data' => [['foo' => 'bar'], ['baz' => 'bak']]],
'OK',
(object) [
'status' => CommandResponse::STATUS_OK,
'message' => 'Nice!',
'data' => [['foo' => 'bar'], ['baz' => 'bak']],
],
CommandResponse::STATUS_OK,
'Nice!',
[['foo' => 'bar'], ['baz' => 'bak']],
],
'Error response with status and message' => [
(object) ['status' => 'ERROR', 'message' => 'DuplicateKeyError(Duplicate key error collection)'],
'ERROR',
'DuplicateKeyError(Duplicate key error collection)',
'Invalid error response with status and message' => [
(object) ['status' => CommandResponse::STATUS_ERROR, 'message' => 'Internal unhandled error'],
CommandResponse::STATUS_ERROR,
'Internal unhandled error',
[],
],
];
}

/**
* @test
* @dataProvider provideResponseStatuses
*/
public function shouldDetectSuccessfulResponse(string $status, bool $shouldBeSuccessful): void
{
$commandResponse = CommandResponse::createFromRawCommandResponseObject((object) ['status' => $status]);

$this->assertSame($shouldBeSuccessful, $commandResponse->isSuccessful());
}

/**
* @return array[]
*/
public function provideResponseStatuses(): array
{
return [
['status' => CommandResponse::STATUS_OK, 'isSuccessful' => true],
['status' => CommandResponse::STATUS_ERROR, 'isSuccessful' => false],
['status' => CommandResponse::STATUS_INVALID, 'isSuccessful' => false],
['status' => CommandResponse::STATUS_SKIPPED, 'isSuccessful' => false] ,
];
}

/** @test */
public function shouldThrowExceptionIfStatusIsMissing(): void
{
Expand Down
93 changes: 93 additions & 0 deletions tests/RequestBuilder/CampaignRequestBuilderTest.php
@@ -0,0 +1,93 @@
<?php declare(strict_types=1);

namespace Lmc\Matej\RequestBuilder;

use Fig\Http\Message\RequestMethodInterface;
use Lmc\Matej\Exception\LogicException;
use Lmc\Matej\Http\RequestManager;
use Lmc\Matej\Model\Command\Sorting;
use Lmc\Matej\Model\Command\UserRecommendation;
use Lmc\Matej\Model\Request;
use Lmc\Matej\Model\Response;
use PHPUnit\Framework\TestCase;

/**
* @covers \Lmc\Matej\RequestBuilder\CampaignRequestBuilder
* @covers \Lmc\Matej\RequestBuilder\AbstractRequestBuilder
*/
class CampaignRequestBuilderTest extends TestCase
{
/** @test */
public function shouldThrowExceptionWhenBuildingEmptyCommands(): void
{
$builder = new CampaignRequestBuilder();

$this->expectException(LogicException::class);
$this->expectExceptionMessage('At least one command must be added to the builder');
$builder->build();
}

/** @test */
public function shouldBuildRequestWithCommands(): void
{
$builder = new CampaignRequestBuilder();

$recommendationCommand1 = UserRecommendation::create('userId1', 1, 'scenario1', 1.0, 600);
$recommendationCommand2 = UserRecommendation::create('userId2', 2, 'scenario2', 0.5, 700);
$recommendationCommand3 = UserRecommendation::create('userId3', 3, 'scenario3', 0.0, 800);
$builder->addRecommendation($recommendationCommand1);
$builder->addRecommendations([$recommendationCommand2, $recommendationCommand3]);

$sortingCommand1 = Sorting::create('userId1', ['itemId1', 'itemId2']);
$sortingCommand2 = Sorting::create('userId2', ['itemId2', 'itemId3']);
$sortingCommand3 = Sorting::create('userId3', ['itemId3', 'itemId4']);

$builder->addSorting($sortingCommand1);
$builder->addSortings([$sortingCommand2, $sortingCommand3]);

$request = $builder->build();

$this->assertInstanceOf(Request::class, $request);
$this->assertSame(RequestMethodInterface::METHOD_POST, $request->getMethod());
$this->assertSame('/campaign', $request->getPath());

$requestData = $request->getData();
$this->assertCount(6, $requestData);
$this->assertContains($recommendationCommand1, $requestData);
$this->assertContains($recommendationCommand2, $requestData);
$this->assertContains($recommendationCommand3, $requestData);
$this->assertContains($sortingCommand1, $requestData);
$this->assertContains($sortingCommand2, $requestData);
$this->assertContains($sortingCommand3, $requestData);
}

/** @test */
public function shouldThrowExceptionWhenSendingCommandsWithoutRequestManager(): void
{
$builder = new CampaignRequestBuilder();

$builder->addSorting(Sorting::create('userId1', ['itemId1', 'itemId2']));

$this->expectException(LogicException::class);
$this->expectExceptionMessage('Instance of RequestManager must be set to request builder');
$builder->send();
}

/** @test */
public function shouldSendRequestViaRequestManager(): void
{
$requestManagerMock = $this->createMock(RequestManager::class);
$requestManagerMock->expects($this->once())
->method('sendRequest')
->with($this->isInstanceOf(Request::class))
->willReturn(new Response(0, 0, 0, 0));

$builder = new CampaignRequestBuilder();
$builder->setRequestManager($requestManagerMock);

$builder->addRecommendation(UserRecommendation::create('userId1', 1, 'scenario1', 1.0, 3600));
$builder->addSorting(Sorting::create('userId1', ['itemId1', 'itemId2']));

$builder->send();
}
}
6 changes: 6 additions & 0 deletions tests/RequestBuilder/RequestBuilderFactoryTest.php
Expand Up @@ -5,6 +5,7 @@
use Lmc\Matej\Http\RequestManager;
use Lmc\Matej\Model\Command\ItemProperty;
use Lmc\Matej\Model\Command\ItemPropertySetup;
use Lmc\Matej\Model\Command\Sorting;
use Lmc\Matej\Model\Request;
use Lmc\Matej\Model\Response;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -57,10 +58,15 @@ public function provideBuilderMethods(): array
$builder->addItemProperty(ItemProperty::create('item-id', []));
};

$campaignInit = function (CampaignRequestBuilder $builder): void {
$builder->addSorting(Sorting::create('item-id', ['item1', 'item2']));
};

return [
['setupItemProperties', ItemPropertiesSetupRequestBuilder::class, $itemPropertiesSetupInit],
['deleteItemProperties', ItemPropertiesSetupRequestBuilder::class, $itemPropertiesSetupInit],
['events', EventsRequestBuilder::class, $eventInit],
['campaign', CampaignRequestBuilder::class, $campaignInit],
];
}
}

0 comments on commit ee6eaf5

Please sign in to comment.