Remove OpenFoodFact client
This commit is contained in:
parent
862b6a4741
commit
384328739e
27 changed files with 0 additions and 1682 deletions
18
openfoodfacts-php-0.2.3/.gitignore
vendored
18
openfoodfacts-php-0.2.3/.gitignore
vendored
|
@ -1,18 +0,0 @@
|
|||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### Example user template template
|
||||
### Example user template
|
||||
/build/
|
||||
|
||||
# IntelliJ project files
|
||||
.idea
|
||||
*.iml
|
||||
out
|
||||
gen### Composer template
|
||||
composer.phar
|
||||
/vendor/
|
||||
/log/
|
||||
/tests/tmp/*
|
||||
|
||||
# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
|
||||
# You may choose to ignore a library lock file https://getcomposer.org/doc/02-libraries.md#lock-file
|
||||
composer.lock
|
|
@ -1,5 +0,0 @@
|
|||
language: php
|
||||
php:
|
||||
- '7.2'
|
||||
before_script: composer install --dev
|
||||
script: vendor/bin/phpunit
|
|
@ -1,3 +0,0 @@
|
|||
## 0.0.1 (June 19, 2016) ##
|
||||
|
||||
* Upload project struct
|
|
@ -1,7 +0,0 @@
|
|||
## Contributing
|
||||
|
||||
1. Fork it ( https://github.com/openfoodfacts/openfoodfacts-php/fork )
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
4. Push to the branch (`git push origin my-new-feature`)
|
||||
5. Create a new Pull Request
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Open Food Facts
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,78 +0,0 @@
|
|||
# openfoodfacts-php
|
||||
![Open Food Facts](https://static.openfoodfacts.org/images/misc/openfoodfacts-logo-en-178x150.png)
|
||||
|
||||
PHP API Wrapper for [Open Food Facts](https://openfoodfacts.org/), the open database about food.
|
||||
|
||||
[![Project Status](http://opensource.box.com/badges/active.svg)](http://opensource.box.com/badges)
|
||||
[![Build Status](https://travis-ci.org/openfoodfacts/openfoodfacts-php.svg?branch=master)](https://travis-ci.org/openfoodfacts/openfoodfacts-php)
|
||||
[![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/openfoodfacts/openfoodfacts-php.svg)](https://isitmaintained.com/project/openfoodfacts/openfoodfacts-php "Average time to resolve an issue")
|
||||
[![Percentage of issues still open](https://isitmaintained.com/badge/open/openfoodfacts/openfoodfacts-php.svg)](https://isitmaintained.com/project/openfoodfacts/openfoodfacts-php "Percentage of issues still open")
|
||||
|
||||
## Installation
|
||||
|
||||
With Composer:
|
||||
|
||||
```bash
|
||||
composer require openfoodfacts/openfoodfacts-php
|
||||
```
|
||||
|
||||
## Usage
|
||||
This is the most basic way of creating the API:
|
||||
```php
|
||||
$api = new OpenFoodFacts\Api('food','fr');
|
||||
$product = $api->getProduct('3057640385148');
|
||||
```
|
||||
In the example above you access the "food" database, limited to the French language/country scope.
|
||||
The first parameter is either
|
||||
- "food"
|
||||
- "beauty" or
|
||||
- "pet"
|
||||
|
||||
to decide which product database you want to use.
|
||||
|
||||
The second parameter decides the language/country scope of the chosen database: f.e. "world" or "de" or "fr".
|
||||
|
||||
For more details on this topic: see the [API Documentation](https://en.wiki.openfoodfacts.org/API/Read#Countries_and_Language_of_the_Response)
|
||||
|
||||
These are all the parameters you really need for basic usage.
|
||||
|
||||
As return types for ```$api->getProduct``` you get an ```Document::class``` Object.
|
||||
This may also be an Object of Type ```FoodProduct::class```,```PetProduct::class```, ```BeautyProduct::class``` depending on which API you are creating.
|
||||
These objects inherit from the more generic ```Document::class```
|
||||
|
||||
In the example above, we use the 'food' API and there will get a ```FoodProduct::class```
|
||||
|
||||
For getting a first overview the ```Document::class``` has a function to return an array representation(sorted) for a first start.
|
||||
```php
|
||||
$product = $api->getProduct('3057640385148');
|
||||
$productDataAsArray = $product->getData();
|
||||
```
|
||||
|
||||
|
||||
#### Optional Parameters
|
||||
The other parameters are optional and for a more sophisticated use of the api (from a software development point of view):
|
||||
|
||||
An example in code is found here: [cached_example.php](examples/01-basic_api_usage/cached_example.php)
|
||||
|
||||
LoggerInterface: A logger which decieds where to log errors to (file, console , etc)
|
||||
|
||||
see: [PSR-3 Loggerinterface](https://www.php-fig.org/psr/psr-3/)
|
||||
|
||||
ClientInterface: The HTTP Client - to adjust the connection configs to your needs and more
|
||||
|
||||
see: [Guzzle HTTP Client](https://packagist.org/packages/guzzlehttp/guzzle)
|
||||
|
||||
CacheInterface: To temporarily save the results of API request to improve the performance and to reduce the load on the API- Server
|
||||
|
||||
see: [PSR-16 Simple Cache](https://www.php-fig.org/psr/psr-16/)
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
### Contributing
|
||||
|
||||
1. Fork it ( https://github.com/openfoodfacts/openfoodfacts-php/fork )
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
4. Push to the branch (`git push origin my-new-feature`)
|
||||
5. Create a new Pull Request
|
|
@ -1,41 +0,0 @@
|
|||
{
|
||||
"name": "openfoodfacts/openfoodfacts-php",
|
||||
"description": "Open Food Facts API Wrapper, the open database about food.",
|
||||
"homepage": "https://world.openfoodfacts.org/",
|
||||
"type": "library",
|
||||
"minimum-stability": "stable",
|
||||
"license": "MIT",
|
||||
"support": {
|
||||
"email": "rmorenp@rampamster.org",
|
||||
"issues": "https://github.com/openfoodfacts/openfoodfacts-php/issues",
|
||||
"chat": "https://slack.openfoodfacts.org/",
|
||||
"source": "https://github.com/openfoodfacts/openfoodfacts-php"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Roberto Moreno",
|
||||
"email": "rmorenp@rampmaster.org",
|
||||
"homepage": "http://www.rampmaster.org",
|
||||
"role": "Wrapper Developer"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"guzzlehttp/guzzle": "^6.3",
|
||||
"psr/log": "^1.0",
|
||||
"psr/simple-cache": "^1.0",
|
||||
"ext-json": "*",
|
||||
"ext-curl": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"OpenFoodFacts\\": "src/"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.0",
|
||||
"symfony/cache": "^4.3",
|
||||
"monolog/monolog": "^1.23",
|
||||
"ext-gd": ">=7.2"
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
TODO
|
|
@ -1,13 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>OFF</title>
|
||||
</head>
|
||||
<body>
|
||||
<form action="./request.php" method="get">
|
||||
<input type="text" class="form-control" placeholder="barcode" name="ean13">
|
||||
<input type="submit" name="Ok" value="Find this Barcode !">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
// Default parameters by case
|
||||
$country = 'fr'; // Country by using OFF
|
||||
$productSlug = 'produit'; // Product by language (producto in spanish or product in english)
|
||||
|
||||
// Format URL
|
||||
$url = 'https://{country}.openfoodfacts.org/api/v0/{product}/{scan}.json';
|
||||
|
||||
// Where we will set the value of the scan
|
||||
$barcode = (int) $_GET['ean13'];
|
||||
|
||||
$url = str_replace(['{country}','{product}','{scan}'],[$country,$productSlug,$barcode],$url);
|
||||
|
||||
// Connection to the API (french version here)
|
||||
$result = file_get_contents($url);
|
||||
|
||||
// Decoding the JSON into an usable array (the value "true" confirms that the return is only an array)
|
||||
$json = json_decode($result, true);
|
||||
|
||||
// Get the datas we want
|
||||
$productName = $json['product']['product_name'];
|
||||
$brand = $json['product']['brands'];
|
||||
$image = $json['product']['image_small_url'];
|
||||
|
||||
$viewData = file_get_contents('response.html');
|
||||
|
||||
echo str_replace(
|
||||
['{productName}','{brand}','{image}','{json}'],
|
||||
[$productName,$brand,$image,print_r($json,true)],
|
||||
$viewData);
|
|
@ -1,28 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>OFF</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Example Output</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Product Name</td>
|
||||
<td>{productName}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Brand</td>
|
||||
<td>{brand}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Image</td>
|
||||
<td><img src="{image}"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2>Response Struct (Array Format)</h2>
|
||||
<pre>
|
||||
{json}
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,13 +0,0 @@
|
|||
|
||||
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
use Symfony\Component\Cache\Psr16Cache;
|
||||
|
||||
include '../../vendor/autoload.php';
|
||||
$logger = new \Monolog\Logger('test');
|
||||
$httpClient = new \GuzzleHttp\Client();
|
||||
// the PSR-6 cache object that you want to use (you might also use a PSR-16 Interface Object directly)
|
||||
$psr6Cache = new FilesystemAdapter();
|
||||
$psr16Cache = new Psr16Cache($psr6Cache);
|
||||
$api = new \OpenFoodFacts\Api('food', 'world', $logger, $httpClient, $psr16Cache);
|
||||
$product = $api->getProduct(rand(1, 50));
|
|
@ -1,36 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.8/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
colors="true"
|
||||
bootstrap="vendor/autoload.php"
|
||||
>
|
||||
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
<server name="KERNEL_CLASS" value="AppKernel" />
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="AllTests">
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>src</directory>
|
||||
<!--<exclude>
|
||||
<directory>src/*Bundle/Resources</directory>
|
||||
</exclude>-->
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
<logging>
|
||||
<log type="coverage-html" target="./build/coverrage" />
|
||||
<log type="coverage-clover" target="./build/logs/clover.xml" />
|
||||
<log type="junit" target="./build/logs/junit.xml" />
|
||||
<log type="testdox-html" target="./build/logstestdox.html" />
|
||||
</logging>
|
||||
</phpunit>
|
|
@ -1,536 +0,0 @@
|
|||
<?php /** @noinspection ALL */
|
||||
|
||||
namespace OpenFoodFacts;
|
||||
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\TransferStats;
|
||||
use OpenFoodFacts\Exception\BadRequestException;
|
||||
use OpenFoodFacts\Exception\ProductNotFoundException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Psr\SimpleCache\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* this class provide [...]
|
||||
*
|
||||
* It a fork of the python OpenFoodFact rewrite on PHP 7.2
|
||||
*/
|
||||
class Api
|
||||
{
|
||||
|
||||
/**
|
||||
* the httpClient for all http request
|
||||
* @var ClientInterface
|
||||
*/
|
||||
private $httpClient;
|
||||
|
||||
/**
|
||||
* this property store the current base of the url
|
||||
* @var string
|
||||
*/
|
||||
private $geoUrl = 'https://%s.openfoodfacts.org';
|
||||
|
||||
/**
|
||||
* this property store the current API (it could be : food/beauty/pet )
|
||||
* @var string
|
||||
*/
|
||||
private $currentAPI = '';
|
||||
|
||||
/**
|
||||
* This property store the current location for http call
|
||||
*
|
||||
* This property could be world for all product or you can specify le country code (cc) and
|
||||
* language of the interface (lc). If you want filter on french product you can set fr as country code.
|
||||
* We strongly recommend to use english as language of the interface
|
||||
*
|
||||
* @example fr-en
|
||||
* @link https://en.wiki.openfoodfacts.org/API/Read#Country_code_.28cc.29_and_Language_of_the_interface_.28lc.29
|
||||
* @var string
|
||||
*/
|
||||
private $geography = 'world';
|
||||
|
||||
/**
|
||||
* this property store the auth parameter (username and password)
|
||||
* @var array
|
||||
*/
|
||||
private $auth = null;
|
||||
|
||||
/**
|
||||
* this property help you to log information
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger = null;
|
||||
|
||||
/**
|
||||
* this constant defines the environments usable by the API
|
||||
* @var array
|
||||
*/
|
||||
private const LIST_API = [
|
||||
'food' => 'https://%s.openfoodfacts.org',
|
||||
'beauty' => 'https://%s.openbeautyfacts.org',
|
||||
'pet' => 'https://%s.openpetfoodfacts.org',
|
||||
'product' => 'https://%s.openproductsfacts.org',
|
||||
];
|
||||
|
||||
/**
|
||||
* This constant defines the facets usable by the API
|
||||
*
|
||||
* This variable is used to create the magic functions like "getIngredients" or "getBrands"
|
||||
* @var array
|
||||
*/
|
||||
private const FACETS = [
|
||||
'additives',
|
||||
'allergens',
|
||||
'brands',
|
||||
'categories',
|
||||
'countries',
|
||||
'contributors',
|
||||
'code',
|
||||
'entry_dates',
|
||||
'ingredients',
|
||||
'label',
|
||||
'languages',
|
||||
'nutrition_grade',
|
||||
'packaging',
|
||||
'packaging_codes',
|
||||
'purchase_places',
|
||||
'photographer',
|
||||
'informer',
|
||||
'states',
|
||||
'stores',
|
||||
'traces',
|
||||
];
|
||||
|
||||
/**
|
||||
* This constant defines the extensions authorized for the downloading of the data
|
||||
* @var array
|
||||
*/
|
||||
private const FILE_TYPE_MAP = [
|
||||
"mongodb" => "openfoodfacts-mongodbdump.tar.gz",
|
||||
"csv" => "en.openfoodfacts.org.products.csv",
|
||||
"rdf" => "en.openfoodfacts.org.products.rdf"
|
||||
];
|
||||
|
||||
/**
|
||||
* the constructor of the function
|
||||
*
|
||||
* @param string $api the environment to search
|
||||
* @param string $geography this parameter represent the the country code and the interface of the language
|
||||
* @param LoggerInterface $logger this parameter define an logger
|
||||
* @param ClientInterface|null $clientInterface
|
||||
* @param CacheInterface|null $cacheInterface
|
||||
*/
|
||||
public function __construct(
|
||||
string $api = 'food',
|
||||
string $geography = 'world',
|
||||
LoggerInterface $logger = null,
|
||||
ClientInterface $clientInterface = null,
|
||||
CacheInterface $cacheInterface = null
|
||||
)
|
||||
{
|
||||
$this->cache = $cacheInterface;
|
||||
$this->logger = $logger ?? new NullLogger();
|
||||
$this->httpClient = $clientInterface ?? new Client();
|
||||
|
||||
$this->geoUrl = sprintf(self::LIST_API[$api], $geography);
|
||||
$this->geography = $geography;
|
||||
$this->currentAPI = $api;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function allows you to perform tests
|
||||
* The domain is correct and for testing purposes only
|
||||
*/
|
||||
public function activeTestMode() : void
|
||||
{
|
||||
$this->geoUrl = 'https://world.openfoodfacts.net';
|
||||
$this->authentification('off', 'off');
|
||||
}
|
||||
|
||||
/**
|
||||
* This function store the authentication parameter
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
*/
|
||||
public function authentification(string $username, string $password) :void
|
||||
{
|
||||
$this->auth = [
|
||||
'user_id' => $username,
|
||||
'password' => $password
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* It's a magic function, it works only for facets
|
||||
* @param string $name The name of the function
|
||||
* @param void $arguments not use yet (probably needed for ingredients)
|
||||
* @return Collection The list of all documents found
|
||||
* @throws InvalidArgumentException
|
||||
* @throws BadRequestException
|
||||
* @example getIngredients()
|
||||
*/
|
||||
public function __call(string $name, $arguments) : Collection
|
||||
{
|
||||
//TODO : test with argument for ingredient
|
||||
if (strpos($name, 'get') === 0) {
|
||||
$facet = strtolower(substr($name, 3));
|
||||
//TODO: what about PSR-12, e.g.: getNutritionGrade() ?
|
||||
|
||||
if (!in_array($facet, self::FACETS)) {
|
||||
throw new BadRequestException('Facet "' . $facet . '" not found');
|
||||
}
|
||||
|
||||
if ($facet === "purchase_places") {
|
||||
$facet = "purchase-places";
|
||||
} elseif ($facet === "packaging_codes") {
|
||||
$facet = "packager-codes";
|
||||
} elseif ($facet === "entry_dates") {
|
||||
$facet = "entry-dates";
|
||||
}
|
||||
|
||||
$url = $this->buildUrl(null, $facet, []);
|
||||
$result = $this->fetch($url);
|
||||
if ($facet !== 'ingredients') {
|
||||
$result = [
|
||||
'products' => $result['tags'],
|
||||
'count' => $result['count'],
|
||||
'page' => 1,
|
||||
'skip' => 0,
|
||||
'page_size' => $result['count'],
|
||||
];
|
||||
}
|
||||
return new Collection($result, $this->currentAPI);
|
||||
}
|
||||
|
||||
throw new BadRequestException('Call to undefined method '.__CLASS__.'::'.$name.'()');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* this function search an Document by barcode
|
||||
* @param string $barcode the barcode [\d]{13}
|
||||
* @return Document A Document if found
|
||||
* @throws InvalidArgumentException
|
||||
* @throws ProductNotFoundException
|
||||
* @throws BadRequestException
|
||||
*/
|
||||
public function getProduct(string $barcode) : Document
|
||||
{
|
||||
$url = $this->buildUrl('api', 'product', $barcode);
|
||||
|
||||
$rawResult = $this->fetch($url);
|
||||
if ($rawResult['status'] === 0) {
|
||||
//TODO: maybe return null here? (just throw an exception if something really went wrong?
|
||||
throw new ProductNotFoundException("Product not found", 1);
|
||||
}
|
||||
|
||||
return Document::createSpecificDocument($this->currentAPI, $rawResult['product']);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function return a Collection of Document search by facets
|
||||
* @param array $query list of facets with value
|
||||
* @param integer $page Number of the page
|
||||
* @return Collection The list of all documents found
|
||||
* @throws InvalidArgumentException
|
||||
* @throws BadRequestException
|
||||
*/
|
||||
public function getByFacets(array $query = [], int $page = 1) : Collection
|
||||
{
|
||||
if (empty($query)) {
|
||||
return new Collection();
|
||||
}
|
||||
$search = [];
|
||||
ksort($query);
|
||||
foreach ($query as $key => $value) {
|
||||
$search[] = $key;
|
||||
$search[] = $value;
|
||||
}
|
||||
|
||||
$url = $this->buildUrl(null, $search, $page);
|
||||
$result = $this->fetch($url);
|
||||
return new Collection($result, $this->currentAPI);
|
||||
}
|
||||
|
||||
/**
|
||||
* this function help you to add a new product (or update ??)
|
||||
* @param array $postData The post data
|
||||
* @return bool|string bool if the product has been added or the error message
|
||||
* @throws BadRequestException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function addNewProduct(array $postData)
|
||||
{
|
||||
if (!isset($postData['code']) || !isset($postData['product_name'])) {
|
||||
throw new BadRequestException('code or product_name not found!');
|
||||
}
|
||||
|
||||
$url = $this->buildUrl('cgi', 'product_jqm2.pl', []);
|
||||
$result = $this->fetchPost($url, $postData);
|
||||
|
||||
if ($result['status_verbose'] === 'fields saved' && $result['status'] === 1) {
|
||||
return true;
|
||||
}
|
||||
return $result['status_verbose'];
|
||||
}
|
||||
|
||||
/**
|
||||
* [uploadImage description]
|
||||
* @param string $code the barcode of the product
|
||||
* @param string $imageField th name of the image
|
||||
* @param string $imagePath the path of the image
|
||||
* @return array the http post response (cast in array)
|
||||
* @throws BadRequestException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function uploadImage(string $code, string $imageField, string $imagePath)
|
||||
{
|
||||
//TODO : need test
|
||||
if ($this->currentAPI !== 'food') {
|
||||
throw new BadRequestException('not Available yet');
|
||||
}
|
||||
if (!in_array($imageField, ["front", "ingredients", "nutrition"])) {
|
||||
throw new BadRequestException('ImageField not valid!');
|
||||
}
|
||||
if (!file_exists($imagePath)) {
|
||||
throw new BadRequestException('Image not found');
|
||||
}
|
||||
|
||||
|
||||
$url = $this->buildUrl('cgi', 'product_image_upload.pl', []);
|
||||
$postData = [
|
||||
'code' => $code,
|
||||
'imagefield' => $imageField,
|
||||
'imgupload_' . $imageField => fopen($imagePath, 'r')
|
||||
];
|
||||
return $this->fetchPost($url, $postData, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* A search function
|
||||
* @param string $search a search term (fulltext)
|
||||
* @param integer $page Number of the page
|
||||
* @param integer $pageSize The page size
|
||||
* @param string $sortBy the sort
|
||||
* @return Collection The list of all documents found
|
||||
* @throws BadRequestException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function search(string $search, int $page = 1, int $pageSize = 20, string $sortBy = 'unique_scans')
|
||||
{
|
||||
$parameters = [
|
||||
'search_terms' => $search,
|
||||
'page' => $page,
|
||||
'page_size' => $pageSize,
|
||||
'sort_by' => $sortBy,
|
||||
'json' => '1',
|
||||
];
|
||||
|
||||
$url = $this->buildUrl('cgi', 'search.pl', $parameters);
|
||||
$result = $this->fetch($url, false);
|
||||
return new Collection($result, $this->currentAPI);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function download all data from OpenFoodFact
|
||||
* @param string $filePath the location where you want to put the stream
|
||||
* @param string $fileType mongodb/csv/rdf
|
||||
* @return bool return true when download is complete
|
||||
* @throws BadRequestException
|
||||
*/
|
||||
public function downloadData(string $filePath, string $fileType = "mongodb")
|
||||
{
|
||||
|
||||
if (!isset(self::FILE_TYPE_MAP[$fileType])) {
|
||||
$this->logger->warning(
|
||||
'OpenFoodFact - fetch - failed - File type not recognized!',
|
||||
['fileType' => $fileType, 'availableTypes' => self::FILE_TYPE_MAP]
|
||||
);
|
||||
throw new BadRequestException('File type not recognized!');
|
||||
}
|
||||
|
||||
$url = $this->buildUrl('data', self::FILE_TYPE_MAP[$fileType]);
|
||||
try {
|
||||
$response = $this->httpClient->request('get', $url, ['sink' => $filePath]);
|
||||
} catch (GuzzleException $guzzleException) {
|
||||
$this->logger->warning(sprintf('OpenFoodFact - fetch - failed - GET : %s', $url), ['exception' => $guzzleException]);
|
||||
$exception = new BadRequestException($guzzleException->getMessage(), $guzzleException->getCode(), $guzzleException);
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
$this->logger->info('OpenFoodFact - fetch - GET : ' . $url . ' - ' . $response->getStatusCode());
|
||||
|
||||
//TODO: validate response here (server may respond with 200 - OK but you might not get valid data as a response)
|
||||
|
||||
return $response->getStatusCode() == 200;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This private function do a http request
|
||||
* @param string $url the url to fetch
|
||||
* @param boolean $isJsonFile the request must be finish by '.json' ?
|
||||
* @return array return the result of the request in array format
|
||||
* @throws InvalidArgumentException
|
||||
* @throws BadRequestException
|
||||
*/
|
||||
private function fetch(string $url, bool $isJsonFile = true) : array
|
||||
{
|
||||
|
||||
$url .= ($isJsonFile? '.json' : '');
|
||||
$realUrl = $url;
|
||||
$cacheKey = md5($realUrl);
|
||||
|
||||
if (!empty($this->cache) && $this->cache->has($cacheKey)) {
|
||||
$cachedResult = $this->cache->get($cacheKey);
|
||||
return $cachedResult;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'on_stats' => function (TransferStats $stats) use (&$realUrl) {
|
||||
// this function help to find redirection
|
||||
// On redirect we lost some parameters like page
|
||||
$realUrl= (string)$stats->getEffectiveUri();
|
||||
}
|
||||
];
|
||||
if ($this->auth) {
|
||||
$data['auth'] = array_values($this->auth);
|
||||
}
|
||||
|
||||
try {
|
||||
$response = $this->httpClient->request('get', $url, $data);
|
||||
} catch (GuzzleException $guzzleException) {
|
||||
$this->logger->warning(sprintf('OpenFoodFact - fetch - failed - GET : %s', $url), ['exception' => $guzzleException]);
|
||||
//TODO: What to do on a error? - return empty array?
|
||||
$exception = new BadRequestException($guzzleException->getMessage(), $guzzleException->getCode(), $guzzleException);
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
if ($realUrl !== $url) {
|
||||
$this->logger->warning('OpenFoodFact - The url : '. $url . ' has been redirect to ' . $realUrl);
|
||||
trigger_error('OpenFoodFact - Your request has been redirect');
|
||||
}
|
||||
$this->logger->info('OpenFoodFact - fetch - GET : ' . $url . ' - ' . $response->getStatusCode());
|
||||
|
||||
$jsonResult = json_decode($response->getBody(), true);
|
||||
|
||||
if (!empty($this->cache) && !empty($jsonResult)) {
|
||||
$this->cache->set($cacheKey, $jsonResult);
|
||||
}
|
||||
|
||||
return $jsonResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function performs the same job of the "fetch" function except the call method and parameters
|
||||
* @param string $url The url to fetch
|
||||
* @param array $postData The post data
|
||||
* @param boolean $isMultipart The data is multipart ?
|
||||
* @return array return the result of the request in array format
|
||||
* @throws InvalidArgumentException
|
||||
* @throws BadRequestException
|
||||
*/
|
||||
private function fetchPost(string $url, array $postData, bool $isMultipart = false) : array
|
||||
{
|
||||
$data = [];
|
||||
if ($this->auth) {
|
||||
$data['auth'] = array_values($this->auth);
|
||||
}
|
||||
if ($isMultipart) {
|
||||
foreach ($postData as $key => $value) {
|
||||
$data['multipart'][] = [
|
||||
'name' => $key,
|
||||
'contents' => $value
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$data['form_params'] = $postData;
|
||||
}
|
||||
|
||||
$cacheKey = md5($url . json_encode($data));
|
||||
|
||||
if (!empty($this->cache) && $this->cache->has($cacheKey)) {
|
||||
return $this->cache->get($cacheKey);
|
||||
}
|
||||
|
||||
try {
|
||||
$response = $this->httpClient->request('post', $url, $data);
|
||||
}catch (GuzzleException $guzzleException){
|
||||
$exception = new BadRequestException($guzzleException->getMessage(), $guzzleException->getCode(), $guzzleException);
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
$this->logger->info('OpenFoodFact - fetch - GET : ' . $url . ' - ' . $response->getStatusCode());
|
||||
|
||||
$jsonResult = json_decode($response->getBody(), true);
|
||||
|
||||
if (!empty($this->cache) && !empty($jsonResult)) {
|
||||
$this->cache->set($cacheKey, $jsonResult);
|
||||
}
|
||||
|
||||
return $jsonResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* This private function generates an url according to the parameters
|
||||
* @param string|null $service
|
||||
* @param string|array|null $resourceType
|
||||
* @param string|array|null $parameters
|
||||
* @return string the generated url
|
||||
*/
|
||||
private function buildUrl(string $service = null, $resourceType = null, $parameters = null) : string
|
||||
{
|
||||
$baseUrl = null;
|
||||
switch ($service) {
|
||||
case 'api':
|
||||
$baseUrl = implode('/', [
|
||||
$this->geoUrl,
|
||||
$service,
|
||||
'v0',
|
||||
$resourceType,
|
||||
$parameters
|
||||
]);
|
||||
break;
|
||||
case 'data':
|
||||
$baseUrl = implode('/', [
|
||||
$this->geoUrl,
|
||||
$service,
|
||||
$resourceType
|
||||
]);
|
||||
break;
|
||||
case 'cgi':
|
||||
$baseUrl = implode('/', [
|
||||
$this->geoUrl,
|
||||
$service,
|
||||
$resourceType
|
||||
]);
|
||||
$baseUrl .= '?' . http_build_query($parameters);
|
||||
break;
|
||||
case null:
|
||||
default:
|
||||
if (is_array($resourceType)) {
|
||||
$resourceType = implode('/', $resourceType);
|
||||
}
|
||||
if ($resourceType == 'ingredients') {
|
||||
//need test
|
||||
$resourceType = implode('/', ["state", "complete", $resourceType]);
|
||||
$parameters = 1;
|
||||
}
|
||||
$baseUrl = implode('/', array_filter([
|
||||
$this->geoUrl,
|
||||
$resourceType,
|
||||
$parameters
|
||||
], function ($value) {
|
||||
return !empty($value);
|
||||
}));
|
||||
break;
|
||||
}
|
||||
return $baseUrl;
|
||||
}
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace OpenFoodFacts;
|
||||
|
||||
use Iterator;
|
||||
|
||||
class Collection implements Iterator
|
||||
{
|
||||
|
||||
private $listDocuments = null;
|
||||
private $count = null;
|
||||
private $page = null;
|
||||
private $skip = null;
|
||||
private $pageSize = null;
|
||||
|
||||
/**
|
||||
* initialization of the collection
|
||||
* @param array|null $data the raw data
|
||||
* @param string|null $api this information help to type the collection (not use yet)
|
||||
*/
|
||||
public function __construct(array $data = null, string $api = null)
|
||||
{
|
||||
$data = $data ?? [
|
||||
'products' => [],
|
||||
'count' => 0,
|
||||
'page' => 0,
|
||||
'skip' => 0,
|
||||
'page_size' => 0,
|
||||
];
|
||||
$this->listDocuments = [];
|
||||
|
||||
if (!empty($data['products'])) {
|
||||
$currentApi = '';
|
||||
if (null !== $api) {
|
||||
$currentApi = $api;
|
||||
}
|
||||
foreach ($data['products'] as $document) {
|
||||
if($document instanceof Document){
|
||||
$this->listDocuments[] = $document;
|
||||
}elseif (is_array($document)){
|
||||
$this->listDocuments[] = Document::createSpecificDocument($currentApi, $document);
|
||||
}else {
|
||||
throw new \InvalidArgumentException(sprintf('Would expect an OpenFoodFacts\Document Interface or Array here. Got: %s', gettype($document)));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$this->count = $data['count'];
|
||||
$this->page = $data['page'];
|
||||
$this->skip = $data['skip'];
|
||||
$this->pageSize = $data['page_size'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int get the current page
|
||||
*/
|
||||
public function getPage() : int
|
||||
{
|
||||
return $this->page;
|
||||
}
|
||||
/**
|
||||
* @return int get the number of element skipped
|
||||
*/
|
||||
public function getSkip() : int
|
||||
{
|
||||
return $this->skip;
|
||||
}
|
||||
/**
|
||||
* @return int get the number of element by page for this collection
|
||||
*/
|
||||
public function getPageSize() : int
|
||||
{
|
||||
return $this->pageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int the number of element in this Collection
|
||||
*/
|
||||
public function pageCount() : int
|
||||
{
|
||||
return count($this->listDocuments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int the number of element for this search
|
||||
*/
|
||||
public function searchCount() : int
|
||||
{
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Iterator
|
||||
*/
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
reset($this->listDocuments);
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return current($this->listDocuments);
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return key($this->listDocuments);
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
return next($this->listDocuments);
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
$key = key($this->listDocuments);
|
||||
return ($key !== null && $key !== false);
|
||||
}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace OpenFoodFacts;
|
||||
|
||||
/**
|
||||
* In mongoDB all element are object, it not possible to define property.
|
||||
* All property of the mongodb entity are store in one property of this class and the magic call try to access to it
|
||||
*/
|
||||
class Document
|
||||
{
|
||||
use RecursiveSortingTrait;
|
||||
|
||||
/**
|
||||
* the whole data
|
||||
* @var array
|
||||
*/
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* the whole data
|
||||
* @var array
|
||||
*/
|
||||
private $api;
|
||||
|
||||
/**
|
||||
* Initialization the document and specify from which API it was extract
|
||||
* @param array $data the whole data
|
||||
* @param string $api the api name
|
||||
*/
|
||||
public function __construct(array $data, string $api = null)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->api = $api;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function __get(string $name)
|
||||
{
|
||||
return $this->data[$name];
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function __isset(string $name):bool
|
||||
{
|
||||
return isset($this->data[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sorted representation of the complete Document Data
|
||||
* @return array
|
||||
*/
|
||||
public function getData(): array
|
||||
{
|
||||
$this->recursiveSortArray($this->data);
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Document in the type regarding to the API used.
|
||||
* May be a Child of "Document" e.g.: FoodDocument or ProductDocument
|
||||
* @param string $apiIdentifier
|
||||
* @param array $data
|
||||
* @return Document
|
||||
*/
|
||||
public static function createSpecificDocument(string $apiIdentifier, array $data): Document
|
||||
{
|
||||
if ($apiIdentifier === '') {
|
||||
return new Document($data, $apiIdentifier);
|
||||
}
|
||||
|
||||
$className = "OpenFoodFacts\Document\\" . ucfirst($apiIdentifier) . 'Document';
|
||||
|
||||
if (class_exists($className) && is_subclass_of($className, Document::class)) {
|
||||
return new $className($data, $apiIdentifier);
|
||||
}
|
||||
|
||||
return new Document($data, $apiIdentifier);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace OpenFoodFacts\Document;
|
||||
|
||||
use OpenFoodFacts\Document;
|
||||
|
||||
class BeautyDocument extends Document
|
||||
{
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace OpenFoodFacts\Document;
|
||||
|
||||
use OpenFoodFacts\Document;
|
||||
|
||||
class FoodDocument extends Document
|
||||
{
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace OpenFoodFacts\Document;
|
||||
|
||||
use OpenFoodFacts\Document;
|
||||
|
||||
class PetDocument extends Document
|
||||
{
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace OpenFoodFacts\Document;
|
||||
|
||||
use OpenFoodFacts\Document;
|
||||
|
||||
class ProductDocument extends Document
|
||||
{
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace OpenFoodFacts\Exception;
|
||||
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Just an exception class for the try catch
|
||||
*/
|
||||
class BadRequestException extends Exception
|
||||
{
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace OpenFoodFacts\Exception;
|
||||
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Just an exception class for the try catch
|
||||
*/
|
||||
class ProductNotFoundException extends Exception
|
||||
{
|
||||
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace OpenFoodFacts;
|
||||
|
||||
trait FilesystemTrait
|
||||
{
|
||||
function recursiveDeleteDirectory($dir)
|
||||
{
|
||||
if (is_dir($dir)) {
|
||||
$objects = scandir($dir);
|
||||
foreach ($objects as $object) {
|
||||
if ($object != "." && $object != "..") {
|
||||
if (is_dir($dir . "/" . $object)) {
|
||||
$this->recursiveDeleteDirectory($dir . "/" . $object);
|
||||
} else {
|
||||
unlink($dir . "/" . $object);
|
||||
}
|
||||
}
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace OpenFoodFacts;
|
||||
|
||||
/**
|
||||
* Trait RecursiveSortingTrait
|
||||
*/
|
||||
trait RecursiveSortingTrait
|
||||
{
|
||||
/**
|
||||
* @param array $arr
|
||||
* @return bool
|
||||
*/
|
||||
private function isAssoc(array $arr): bool
|
||||
{
|
||||
return array_keys($arr) !== range(0, count($arr) - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts referenced array of arrays in a recursive way for better understandability
|
||||
* @param array $arr
|
||||
* @see ksort
|
||||
* @see asort
|
||||
*/
|
||||
public function recursiveSortArray(array &$arr): void
|
||||
{
|
||||
if ($this->isAssoc($arr)) {
|
||||
ksort($arr);
|
||||
} else {
|
||||
asort($arr);
|
||||
}
|
||||
foreach ($arr as &$a) {
|
||||
if (is_array($a)) {
|
||||
$this->recursiveSortArray($a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,178 +0,0 @@
|
|||
<?php
|
||||
|
||||
use GuzzleHttp\Exception\ServerException;
|
||||
use OpenFoodFacts\FilesystemTrait;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
use OpenFoodFacts\Api;
|
||||
use OpenFoodFacts\Collection;
|
||||
use OpenFoodFacts\Document\FoodDocument;
|
||||
use OpenFoodFacts\Document;
|
||||
use OpenFoodFacts\Exception\{
|
||||
ProductNotFoundException,
|
||||
BadRequestException
|
||||
};
|
||||
|
||||
|
||||
use Monolog\Logger;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
use Symfony\Component\Cache\Psr16Cache;
|
||||
use Symfony\Component\Console\Logger\ConsoleLogger;
|
||||
|
||||
class ApiFoodCacheTest extends TestCase
|
||||
{
|
||||
use FilesystemTrait;
|
||||
|
||||
/**
|
||||
* @var Api
|
||||
*/
|
||||
private $api;
|
||||
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
@rmdir('tests/tmp');
|
||||
@mkdir('tests/tmp');
|
||||
@mkdir('tests/tmp/cache');
|
||||
$log = new Logger('name');
|
||||
$log->pushHandler(new StreamHandler('log/test.log'));
|
||||
$psr6Cache = new FilesystemAdapter(sprintf('testrun_%u', rand(0, 1000)), 10, 'tests/tmp/cache');
|
||||
$cache = new Psr16Cache($psr6Cache);
|
||||
|
||||
$httpClient = new GuzzleHttp\Client([
|
||||
// "http_errors" => false, // MUST not use as it crashes error handling
|
||||
'Connection' => 'close',
|
||||
CURLOPT_FORBID_REUSE => true,
|
||||
CURLOPT_FRESH_CONNECT => true,
|
||||
'defaults' => [
|
||||
'headers' => [
|
||||
'CURLOPT_USERAGENT' => 'OFF - PHP - SDK - Unit Test',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$api = new Api('food', 'fr-en', $log, $httpClient, $cache);
|
||||
$this->assertInstanceOf(Api::class, $api);
|
||||
$this->api = $api;
|
||||
|
||||
}
|
||||
|
||||
public function testApi(): void
|
||||
{
|
||||
|
||||
$prd = $this->api->getProduct('3057640385148');
|
||||
|
||||
$this->assertInstanceOf(FoodDocument::class, $prd);
|
||||
$this->assertInstanceOf(Document::class, $prd);
|
||||
|
||||
$this->assertTrue(isset($prd->product_name));
|
||||
$this->assertNotEmpty($prd->product_name);
|
||||
|
||||
try {
|
||||
$product = $this->api->getProduct('305764038514800');
|
||||
$this->assertTrue(false);
|
||||
} catch (ProductNotFoundException $e) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $this->api->downloadData('tests/mongodb', 'nopeFile');
|
||||
$this->assertTrue(false);
|
||||
} catch (BadRequestException $e) {
|
||||
$this->assertEquals($e->getMessage(), 'File type not recognized!');
|
||||
}
|
||||
|
||||
// $result = $this->api->downloadData('tests/tmp/mongodb');
|
||||
// $this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testApiCollection(): void
|
||||
{
|
||||
|
||||
$collection = $this->api->getByFacets([]);
|
||||
$this->assertInstanceOf(Collection::class, $collection);
|
||||
$this->assertEquals($collection->pageCount(), 0);
|
||||
|
||||
try {
|
||||
$collection = $this->api->getByFacets(['trace' => 'egg', 'country' => 'france'], 3);
|
||||
$this->assertTrue(false);
|
||||
} catch (\PHPUnit\Framework\Error\Notice $e) {
|
||||
$this->assertEquals($e->getMessage(), 'OpenFoodFact - Your request has been redirect');
|
||||
}
|
||||
|
||||
$collection = $this->api->getByFacets(['trace' => 'eggs', 'country' => 'france'], 3);
|
||||
$this->assertInstanceOf(Collection::class, $collection);
|
||||
$this->assertEquals($collection->pageCount(), 20);
|
||||
$this->assertEquals($collection->getPage(), 3);
|
||||
$this->assertEquals($collection->getSkip(), 40);
|
||||
$this->assertEquals($collection->getPageSize(), 20);
|
||||
$this->assertGreaterThan(1000, $collection->searchCount());
|
||||
|
||||
foreach ($collection as $key => $doc) {
|
||||
if ($key > 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->assertInstanceOf(FoodDocument::class, $doc);
|
||||
$this->assertInstanceOf(Document::class, $doc);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function testApiSearch(): void
|
||||
{
|
||||
|
||||
$collection = $this->api->search('volvic', 3, 30);
|
||||
$this->assertInstanceOf(Collection::class, $collection);
|
||||
$this->assertEquals($collection->pageCount(), 30);
|
||||
$this->assertGreaterThan(100, $collection->searchCount());
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testFacets(): void
|
||||
{
|
||||
|
||||
$collection = $this->api->getIngredients();
|
||||
$this->assertInstanceOf(Collection::class, $collection);
|
||||
$this->assertEquals($collection->pageCount(), 20);
|
||||
$this->assertEquals($collection->getPageSize(), 20);
|
||||
$this->assertGreaterThan(70000, $collection->searchCount());
|
||||
|
||||
try {
|
||||
$collection = $this->api->getIngredient();
|
||||
$this->assertInstanceOf(Collection::class, $collection);
|
||||
$this->assertTrue(false);
|
||||
} catch (BadRequestException $e) {
|
||||
$this->assertEquals($e->getMessage(), 'Facet "ingredient" not found');
|
||||
}
|
||||
|
||||
$collection = $this->api->getPurchase_places();
|
||||
$this->assertInstanceOf(Collection::class, $collection);
|
||||
$collection = $this->api->getPackaging_codes();
|
||||
$this->assertInstanceOf(Collection::class, $collection);
|
||||
$collection = $this->api->getEntry_dates();
|
||||
$this->assertInstanceOf(Collection::class, $collection);
|
||||
|
||||
try {
|
||||
$collection = $this->api->getIngredient();
|
||||
$this->assertTrue(false);
|
||||
} catch (BadRequestException $e) {
|
||||
$this->assertEquals($e->getMessage(), 'Facet "ingredient" not found');
|
||||
}
|
||||
|
||||
try {
|
||||
$collection = $this->api->nope();
|
||||
} catch (\Exception $e) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->recursiveDeleteDirectory('tests/tmp');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,248 +0,0 @@
|
|||
<?php
|
||||
|
||||
use GuzzleHttp\Exception\ServerException;
|
||||
use OpenFoodFacts\FilesystemTrait;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
use OpenFoodFacts\Api;
|
||||
use OpenFoodFacts\Collection;
|
||||
use OpenFoodFacts\Document\FoodDocument;
|
||||
use OpenFoodFacts\Document;
|
||||
use OpenFoodFacts\Exception\{
|
||||
ProductNotFoundException,
|
||||
BadRequestException
|
||||
};
|
||||
|
||||
|
||||
use Monolog\Logger;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
|
||||
class ApiFoodTest extends TestCase
|
||||
{
|
||||
|
||||
use FilesystemTrait;
|
||||
|
||||
private $api;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$log = new Logger('name');
|
||||
$log->pushHandler(new StreamHandler('log/test.log'));
|
||||
|
||||
$this->api = new Api('food', 'fr-en', $log);
|
||||
@rmdir('tests/tmp');
|
||||
@mkdir('tests/tmp');
|
||||
}
|
||||
|
||||
public function testApi(): void
|
||||
{
|
||||
|
||||
$prd = $this->api->getProduct('3057640385148');
|
||||
|
||||
$this->assertInstanceOf(FoodDocument::class, $prd);
|
||||
$this->assertInstanceOf(Document::class, $prd);
|
||||
$this->assertTrue(isset($prd->product_name));
|
||||
$this->assertNotEmpty($prd->product_name);
|
||||
|
||||
try {
|
||||
$product = $this->api->getProduct('305764038514800');
|
||||
$this->assertTrue(false);
|
||||
} catch (ProductNotFoundException $e) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $this->api->downloadData('tests/mongodb', 'nopeFile');
|
||||
$this->assertTrue(false);
|
||||
} catch (BadRequestException $e) {
|
||||
$this->assertEquals($e->getMessage(), 'File type not recognized!');
|
||||
}
|
||||
|
||||
// $result = $this->api->downloadData('tests/tmp/mongodb');
|
||||
// $this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testApiCollection(): void
|
||||
{
|
||||
|
||||
$collection = $this->api->getByFacets([]);
|
||||
$this->assertInstanceOf(Collection::class, $collection);
|
||||
$this->assertEquals($collection->pageCount(), 0);
|
||||
|
||||
try {
|
||||
$collection = $this->api->getByFacets(['trace' => 'egg', 'country' => 'france'], 3);
|
||||
$this->assertTrue(false);
|
||||
} catch (\PHPUnit\Framework\Error\Notice $e) {
|
||||
$this->assertEquals($e->getMessage(), 'OpenFoodFact - Your request has been redirect');
|
||||
}
|
||||
|
||||
$collection = $this->api->getByFacets(['trace' => 'eggs', 'country' => 'france'], 3);
|
||||
$this->assertInstanceOf(Collection::class, $collection);
|
||||
$this->assertEquals($collection->pageCount(), 20);
|
||||
$this->assertEquals($collection->getPage(), 3);
|
||||
$this->assertEquals($collection->getSkip(), 40);
|
||||
$this->assertEquals($collection->getPageSize(), 20);
|
||||
$this->assertGreaterThan(1000, $collection->searchCount());
|
||||
|
||||
foreach ($collection as $key => $doc) {
|
||||
if ($key > 1) {
|
||||
break;
|
||||
}
|
||||
$this->assertInstanceOf(FoodDocument::class, $doc);
|
||||
$this->assertInstanceOf(Document::class, $doc);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function testApiAddProduct(): void
|
||||
{
|
||||
$this->api->activeTestMode();
|
||||
try {
|
||||
$prd = $this->api->getProduct('3057640385148');
|
||||
$this->assertInstanceOf(FoodDocument::class, $prd);
|
||||
$this->assertInstanceOf(Document::class, $prd);
|
||||
} catch (Exception $exception) {
|
||||
if ($exception->getPrevious() instanceof ServerException && $exception->getPrevious()->getCode() === 503) {
|
||||
$this->markTestSkipped(
|
||||
'Testing API currently not available.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$postData = ['code' => $prd->code, 'product_name' => $prd->product_name];
|
||||
|
||||
$result = $this->api->addNewProduct($postData);
|
||||
$this->assertTrue(is_bool($result));
|
||||
|
||||
|
||||
$postData = ['product_name' => $prd->product_name];
|
||||
|
||||
try {
|
||||
$result = $this->api->addNewProduct($postData);
|
||||
$this->assertTrue(false);
|
||||
} catch (BadRequestException $e) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
$postData = ['code' => '', 'product_name' => $prd->product_name];
|
||||
$result = $this->api->addNewProduct($postData);
|
||||
$this->assertTrue(is_string($result));
|
||||
$this->assertEquals($result, 'no code or invalid code');
|
||||
|
||||
}
|
||||
|
||||
public function testApiAddImage(): void
|
||||
{
|
||||
|
||||
$this->api->activeTestMode();
|
||||
try {
|
||||
$prd = $this->api->getProduct('3057640385148');
|
||||
$this->assertInstanceOf(Collection::class, $prd);
|
||||
} catch (Exception $exception) {
|
||||
if ($exception->getPrevious() instanceof ServerException && $exception->getPrevious()->getCode() === 503) {
|
||||
$this->markTestSkipped(
|
||||
'Testing API currently not available.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$this->api->uploadImage('3057640385148', 'fronts', 'nothing');
|
||||
$this->assertTrue(false);
|
||||
} catch (BadRequestException $e) {
|
||||
$this->assertEquals($e->getMessage(), 'ImageField not valid!');
|
||||
}
|
||||
try {
|
||||
$this->api->uploadImage('3057640385148', 'front', 'nothing');
|
||||
$this->assertTrue(false);
|
||||
} catch (BadRequestException $e) {
|
||||
$this->assertEquals($e->getMessage(), 'Image not found');
|
||||
}
|
||||
$file1 = $this->createRandomImage();
|
||||
|
||||
$result = $this->api->uploadImage('3057640385148', 'front', $file1);
|
||||
$this->assertEquals($result['status'], 'status ok');
|
||||
$this->assertTrue(isset($result['imagefield']));
|
||||
$this->assertTrue(isset($result['image']));
|
||||
$this->assertTrue(isset($result['image']['imgid']));
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function testApiSearch(): void
|
||||
{
|
||||
|
||||
$collection = $this->api->search('volvic', 3, 30);
|
||||
$this->assertInstanceOf(Collection::class, $collection);
|
||||
$this->assertEquals($collection->pageCount(), 30);
|
||||
$this->assertGreaterThan(100, $collection->searchCount());
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testFacets(): void
|
||||
{
|
||||
|
||||
$collection = $this->api->getIngredients();
|
||||
$this->assertInstanceOf(Collection::class, $collection);
|
||||
$this->assertEquals($collection->pageCount(), 20);
|
||||
$this->assertEquals($collection->getPageSize(), 20);
|
||||
$this->assertGreaterThan(70000, $collection->searchCount());
|
||||
|
||||
try {
|
||||
$collection = $this->api->getIngredient();
|
||||
$this->assertInstanceOf(Collection::class, $collection);
|
||||
$this->assertTrue(false);
|
||||
} catch (BadRequestException $e) {
|
||||
$this->assertEquals($e->getMessage(), 'Facet "ingredient" not found');
|
||||
}
|
||||
|
||||
$collection = $this->api->getPurchase_places();
|
||||
$this->assertInstanceOf(Collection::class, $collection);
|
||||
$collection = $this->api->getPackaging_codes();
|
||||
$this->assertInstanceOf(Collection::class, $collection);
|
||||
$collection = $this->api->getEntry_dates();
|
||||
$this->assertInstanceOf(Collection::class, $collection);
|
||||
|
||||
try {
|
||||
$collection = $this->api->getIngredient();
|
||||
$this->assertTrue(false);
|
||||
} catch (BadRequestException $e) {
|
||||
$this->assertEquals($e->getMessage(), 'Facet "ingredient" not found');
|
||||
}
|
||||
|
||||
try {
|
||||
$collection = $this->api->nope();
|
||||
} catch (\Exception $e) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function createRandomImage(): string
|
||||
{
|
||||
|
||||
$width = 400;
|
||||
$height = 200;
|
||||
|
||||
$imageRes = imagecreatetruecolor($width, $height);
|
||||
for ($row = 0; $row <= $height; $row++) {
|
||||
for ($column = 0; $column <= $width; $column++) {
|
||||
$colour = imagecolorallocate($imageRes, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255));
|
||||
imagesetpixel($imageRes, $column, $row, $colour);
|
||||
}
|
||||
}
|
||||
$path = 'tests/tmp/image_' . time() . '.jpg';
|
||||
if (imagejpeg($imageRes, $path)) {
|
||||
return $path;
|
||||
}
|
||||
throw new \Exception("Error Processing Request", 1);
|
||||
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->recursiveDeleteDirectory('tests/tmp');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
<?php
|
||||
|
||||
use OpenFoodFacts\FilesystemTrait;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
use OpenFoodFacts\Api;
|
||||
use OpenFoodFacts\Collection;
|
||||
use OpenFoodFacts\Document\PetDocument;
|
||||
use OpenFoodFacts\Document;
|
||||
use OpenFoodFacts\Exception\{
|
||||
ProductNotFoundException,
|
||||
BadRequestException
|
||||
};
|
||||
|
||||
|
||||
use Monolog\Logger;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
|
||||
class ApiPetTest extends TestCase
|
||||
{
|
||||
|
||||
use FilesystemTrait;
|
||||
|
||||
private $api;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$log = new Logger('name');
|
||||
$log->pushHandler(new StreamHandler('log/test.log'));
|
||||
|
||||
$this->api = new Api('pet', 'fr', $log);
|
||||
|
||||
foreach (glob('tests/images/*') as $file) {
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
|
||||
public function testApi()
|
||||
{
|
||||
|
||||
$prd = $this->api->getProduct('7613035799738');
|
||||
|
||||
$this->assertInstanceOf(PetDocument::class, $prd);
|
||||
$this->assertInstanceOf(Document::class, $prd);
|
||||
$this->assertTrue(isset($prd->product_name));
|
||||
$this->assertNotEmpty($prd->product_name);
|
||||
|
||||
}
|
||||
|
||||
public function testApiAddImage()
|
||||
{
|
||||
try {
|
||||
$this->api->uploadImage('7613035799738', 'fronts', 'nothing');
|
||||
$this->assertTrue(false);
|
||||
} catch (BadRequestException $e) {
|
||||
$this->assertEquals($e->getMessage(), 'not Available yet');
|
||||
$this->markTestSkipped(
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function testApiSearch()
|
||||
{
|
||||
|
||||
$collection = $this->api->search('chat', 3, 30);
|
||||
|
||||
$this->assertInstanceOf(Collection::class, $collection);
|
||||
$this->assertEquals($collection->pageCount(), 30);
|
||||
$this->assertGreaterThan(100, $collection->searchCount());
|
||||
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->recursiveDeleteDirectory('tests/tmp');
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue