Skip to content

Commit

Permalink
Merge pull request #5 from answear/search-with-image2
Browse files Browse the repository at this point in the history
Add searchByFeature endpoint
  • Loading branch information
jrawska committed Dec 10, 2020
2 parents 4d1f06e + 32bd6bc commit ddedebc
Show file tree
Hide file tree
Showing 12 changed files with 443 additions and 15 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,23 @@ $detectAndFeturesResponse = $searchByImageClient->getSimilar('url');
Your agrument is: `url` - url to the image on which you want to detect products and features.
In result you're getting `DetectAndFeaturesResponse` that contains all detection returned by api.

#### Search by feature

To search products with previously found feature use `searchByFeature`

```php
use Answear\WideEyesBundle\Service\SearchByImageClient;
$detectAndFeturesResponse = $searchByImageClient->searchByFeature('featureId', 'label', 'gender', 'filters');
```

Your agruments are:
* `featureId` - featureId you got form DetectAndFeatures
* `label` - label you got form DetectAndFeatures
* `gender` - gender you got from DetectAndFeatures (optional)
* `filters` - result filters (optional)

In result you're getting `SearchByFeatureResponse` that contains all found products uids meeting your criteria.

Final notes
------------
Expand Down
8 changes: 4 additions & 4 deletions src/DependencyInjection/AnswearWideEyesExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ public function load(array $configs, ContainerBuilder $container): void
$definition = $container->getDefinition(ConfigProvider::class);
$definition->setArguments(
[
$config['similarApiUrl'],
$config['searchByImageApiUrl'],
$config['similar']['apiUrl'],
$config['searchByImage']['apiUrl'],
$config['publicKey'],
$config['similarRequestTimeout'],
$config['searchByImageRequestTimeout'],
$config['similar']['requestTimeout'],
$config['searchByImage']['requestTimeout'],
$config['connectionTimeout'],
]
);
Expand Down
20 changes: 14 additions & 6 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,20 @@ public function getConfigTreeBuilder(): TreeBuilder

$treeBuilder->getRootNode()
->children()
->scalarNode('similarApiUrl')->defaultValue(self::SIMILAR_API_URL)->end()
->scalarNode('searchByImageApiUrl')->defaultValue(self::SEARCH_BY_IMAGE_API_URL)->end()
->scalarNode('publicKey')->cannotBeEmpty()->end()
->floatNode('connectionTimeout')->defaultValue(self::CONNECTION_TIMEOUT)->end()
->floatNode('similarRequestTimeout')->defaultValue(self::SIMILAR_REQUEST_TIMEOUT)->end()
->floatNode('searchByImageRequestTimeout')->defaultValue(self::SEARCH_BY_IMAGE_REQUEST_TIMEOUT)->end()
->scalarNode('publicKey')->cannotBeEmpty()->end()
->floatNode('connectionTimeout')->defaultValue(self::CONNECTION_TIMEOUT)->end()
->arrayNode('similar')->addDefaultsIfNotSet()
->children()
->scalarNode('apiUrl')->defaultValue(self::SIMILAR_API_URL)->end()
->floatNode('requestTimeout')->defaultValue(self::SIMILAR_REQUEST_TIMEOUT)->end()
->end()
->end()
->arrayNode('searchByImage')->addDefaultsIfNotSet()
->children()
->scalarNode('apiUrl')->defaultValue(self::SEARCH_BY_IMAGE_API_URL)->end()
->floatNode('requestTimeout')->defaultValue(self::SEARCH_BY_IMAGE_REQUEST_TIMEOUT)->end()
->end()
->end()
->end();

return $treeBuilder;
Expand Down
35 changes: 35 additions & 0 deletions src/Request/SearchByFeatureRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Answear\WideEyesBundle\Request;

class SearchByFeatureRequest implements Request
{
private string $featureId;
private string $label;
private ?string $gender;
private ?string $filters;

public function __construct(string $featureId, string $label, ?string $gender = null, ?string $filters = null)
{
$this->featureId = $featureId;
$this->label = $label;
$this->gender = $gender;
$this->filters = $filters;
}

public function toJson(): string
{
return json_encode(
array_filter(
[
'featureId' => $this->featureId,
'label' => $this->label,
'gender' => $this->gender,
'filters' => $this->filters,
]
)
);
}
}
43 changes: 43 additions & 0 deletions src/Response/SearchByFeatureResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace Answear\WideEyesBundle\Response;

use Answear\WideEyesBundle\Exception\MalformedResponse;
use Webmozart\Assert\Assert;

class SearchByFeatureResponse
{
private array $uids;

private function __construct(array $uids)
{
Assert::allString($uids);
$this->uids = $uids;
}

public static function fromArray(array $response): SearchByFeatureResponse
{
try {
$products = $response['products'];
$responseUids = array_map(
static function ($item) {
Assert::keyExists($item, 'uid');

return $item['uid'];
},
$products,
);

return new self($responseUids);
} catch (\Throwable $e) {
throw new MalformedResponse($e->getMessage(), $response, $e);
}
}

public function getUids(): array
{
return $this->uids;
}
}
3 changes: 0 additions & 3 deletions src/Service/AbstractClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@

abstract class AbstractClient
{
protected const SEARCH_BY_ID_ENDPOINT = 'v4/SearchById';
protected const DETECT_AND_FEATURES_ENDPOINT = 'v4/DetectAndFeatures';

protected ConfigProvider $configProvider;
protected ClientInterface $guzzle;

Expand Down
16 changes: 16 additions & 0 deletions src/Service/SearchByImageClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@
namespace Answear\WideEyesBundle\Service;

use Answear\WideEyesBundle\Request\DetectAndFeaturesRequest;
use Answear\WideEyesBundle\Request\SearchByFeatureRequest;
use Answear\WideEyesBundle\Response\DetectAndFeaturesResponse;
use Answear\WideEyesBundle\Response\SearchByFeatureResponse;
use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;

class SearchByImageClient extends AbstractClient
{
private const DETECT_AND_FEATURES_ENDPOINT = 'v4/DetectAndFeatures';
private const SEARCH_BY_FEATURE = 'v4/SearchByFeature';

public function __construct(ConfigProvider $configProvider, ?ClientInterface $client = null)
{
parent::__construct(
Expand All @@ -30,4 +35,15 @@ public function detectAndFeatures(string $image): DetectAndFeaturesResponse
$this->request(self::DETECT_AND_FEATURES_ENDPOINT, new DetectAndFeaturesRequest($image))
);
}

public function searchByFeature(
string $featureId,
string $label,
?string $gender = null,
?string $filters = null
): SearchByFeatureResponse {
return SearchByFeatureResponse::fromArray(
$this->request(self::SEARCH_BY_FEATURE, new SearchByFeatureRequest($featureId, $label, $gender, $filters))
);
}
}
2 changes: 2 additions & 0 deletions src/Service/SimilarClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

class SimilarClient extends AbstractClient
{
private const SEARCH_BY_ID_ENDPOINT = 'v4/SearchById';

public function __construct(ConfigProvider $configProvider, ?ClientInterface $client = null)
{
parent::__construct(
Expand Down
37 changes: 37 additions & 0 deletions tests/Unit/Request/SearchByFeatureRequestTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace Answear\WideEyesBundle\Tests\Unit\Request;

use Answear\WideEyesBundle\Request\SearchByFeatureRequest;
use PHPUnit\Framework\TestCase;

class SearchByFeatureRequestTest extends TestCase
{
/**
* @test
*/
public function requestWithoutGenderAndCountryIsCorrect(): void
{
$request = new SearchByFeatureRequest('featureId', 'label');

self::assertSame(
'{"featureId":"featureId","label":"label"}',
$request->toJson()
);
}

/**
* @test
*/
public function requestWithGenderAndCountryIsCorrect(): void
{
$request = new SearchByFeatureRequest('featureId', 'label', 'female', 'pl.inStock == true');

self::assertSame(
'{"featureId":"featureId","label":"label","gender":"female","filters":"pl.inStock == true"}',
$request->toJson()
);
}
}
107 changes: 107 additions & 0 deletions tests/Unit/Response/SearchByFeatureResponseTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

declare(strict_types=1);

namespace Answear\WideEyesBundle\Tests\Unit\Response;

use Answear\WideEyesBundle\Exception\MalformedResponse;
use Answear\WideEyesBundle\Response\SearchByFeatureResponse;
use PHPUnit\Framework\TestCase;

class SearchByFeatureResponseTest extends TestCase
{
/**
* @test
*/
public function correctlyReturnsUids(): void
{
$responseData = [
'products' => [
[
'uid' => 'uid1',
],
[
'uid' => 'uid2',
],
[
'uid' => 'uid3',
],
],
];

$response = SearchByFeatureResponse::fromArray($responseData);

self::assertEquals(
[
'uid1',
'uid2',
'uid3',
],
$response->getUids()
);
}

/**
* @test
*/
public function malformedResponseWithoutUidInOneItem(): void
{
$responseData = [
'products' => [
[
'uid' => 'uid1',
],
[
'id' => 'uid2',
],
[
'uid' => 'uid3',
],
],
];

$this->expectException(MalformedResponse::class);
SearchByFeatureResponse::fromArray($responseData);
}

/**
* @test
*/
public function malformedResponseWithoutUid(): void
{
$responseData = [
'products' => [
[
'result' => 'uid1',
],
[
'result' => 'uid2',
],
],
];

$this->expectException(MalformedResponse::class);
SearchByFeatureResponse::fromArray($responseData);
}

/**
* @test
*/
public function malformedResponseWithoutProducts(): void
{
$responseData = [
[
'uid' => 'uid1',
],
[
'uid' => 'uid2',
],
[
'uid' => 'uid3',
],
];

$this->expectException(MalformedResponse::class);
SearchByFeatureResponse::fromArray($responseData);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use Answear\WideEyesBundle\Service\SearchByImageClient;
use GuzzleHttp\Psr7\Response;

class SearchByImageClientTest extends AbstractClientTest
class SearchByImageClientDetectAndFeatureTest extends AbstractClientTest
{
private const IMAGE_PATH = 'path';

Expand Down Expand Up @@ -58,7 +58,7 @@ public function successfulDetectAndFeatures(): void
/**
* @test
*/
public function responeWithWrongPropertiesInDetecions(): void
public function responseWithWrongPropertiesInDetections(): void
{
$this->guzzleHandler->append(new Response(200, [], $this->prepareNotProperResponse()));

Expand Down
Loading

0 comments on commit ddedebc

Please sign in to comment.