Commit 8f679730 by Евгений

Улучшение #19452

Учет баллов при работе с АПИ
1 parent 478a9c4a
......@@ -3,7 +3,6 @@
namespace App\Jobs;
use App\Service\AdsHandler;
use App\Service\API;
use App\Service\Contract\APIRequest;
use App\Service\HeaderLimits;
use App\Service\Limits;
......@@ -41,9 +40,17 @@ class ProcessCallAPI implements ShouldQueue
try{
$api = API::getInstance($this->api);
//считаем на сколько объектов зарезервировано получение данных
//только их и запрашиваем
$limit = \App\Models\Limits::find($this->limitId);
//те на которые не хватило баллов помещаем в очередь
if ($api = $this->api->chunk($limit->spent)){
dispatch( new ProcessCallLimitedAPI($api));
}
$response = $api->execute();
$limits->acceptRezerv($this->limitId, new HeaderLimits($response->headers()) );
//TODO: обработать результат
//TODO: обработать результат.
// если не хватило баллов на все что хотели запросиь, то в очередь отправляем новый запрос на получение новых данных
AdsHandler::getInstance($this->api)->handle($response);
}catch(\Exception $e ){
//TODO: надо отдельно выделить ошибки вызовов, за которые списываются баллы
......
......@@ -11,10 +11,13 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProcessCallLimitedAPI implements ShouldQueue, ShouldBeUnique
class ProcessCallLimitedAPI implements ShouldQueue//, ShouldBeUnique
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* @var APIRequest
*/
private $api;
/**
......@@ -52,30 +55,37 @@ class ProcessCallLimitedAPI implements ShouldQueue, ShouldBeUnique
return;
}
//вызов АПИ разбиваем на несколько
if ($api = $this->api->chunk($objects)){
//вызываем для остальных объектов вызов АПИ
self::dispatch( new ProcessCallLimitedAPI($this->api->getToken(), $api));
}
//TODO: Вызов нао будет делать после обработки результатов.
// в том случае, если окажется что были получены не все запрошенные объекты
// т.к. мы не знам сколько там на самом деел объектов, может там их 10, при это лимит стоит 2000
// нам разрешено получить 100. Мы запрапшиваем только 100, получаем 10, но если уже сейчас поставим сразу вторую заадчу на получение еще 100
// то выйдет что зря потратим баллы на еще один азпрос
// //вызов АПИ разбиваем на несколько
// if ($api = $this->api->chunk($objects)){
// //вызываем для остальных объектов вызов АПИ
// dispatch( new ProcessCallLimitedAPI($api));
// }
//вызываем очередь получения данных от АПИ
//туда передаем сохраненный лимит
//там уже либо будет удален, если не удастся выполнить запрос
//либо обновятся данные
self::dispatch(new ProcessCallAPI($limitId, $this->api));
dispatch(new ProcessCallAPI($limitId, $this->api));
}
/**
* The unique ID of the job.
*
* @return string
*/
public function uniqueId()
{
return $this->api->getToken()->id;
}
// /**
// * The unique ID of the job.
// *
// * @return string
// */
// public function uniqueId()
// {
// return $this->api->getToken()->id;
// }
private function reRunHour(){
self::dispatch( new ProcessCallLimitedAPI($this->api))->delay(now()->addMinutes(60-date("i")));
$process = new ProcessCallLimitedAPI($this->api);
dispatch( $process )
->delay(now()->addMinutes(60-date("i")));
}
}
......@@ -5,13 +5,22 @@ use App\Service\Contract\APIRequest;
class AdsHandler{
protected static $_instance;
protected $request;
protected function __constructor(APIRequest $request = null){
$this->request = $request;
}
public static function getInstance(APIRequest $request = null){
self::$_instance = new self($request);
return self::$_instance;
}
public function handle($response){
//постраничная выбрка
if ($response->limited()){
$this->request->next($response->limited());
dispatch( new ProcessCallLimitedAPI($this->request));
}
}
}
......@@ -61,9 +61,8 @@ class Limits implements \App\Service\Contract\Limits {
}
/**
* @param string $service
* @param string $method
* @param int $limit
* @param APIRequest $request
* @param int $objects
* @return int
* @throws \Exception
* предполагается что класс работает в очереди.
......@@ -84,6 +83,7 @@ class Limits implements \App\Service\Contract\Limits {
$rezerv->service = $request->getService();
$rezerv->method = $request->getMethod();
$rezerv->spent = $limit;
$rezerv->day = 0;
$rezerv->current = $this->token->limit;
$rezerv->reserved = 1;
$rezerv->save();
......@@ -146,6 +146,8 @@ class Limits implements \App\Service\Contract\Limits {
$limit = new \App\Models\Limits();
$limit->token = $this->token->id;
$limit->service = '';
$limit->method = '';
$limit->spent = $limits->getSpentLimit();
$limit->current = $limits->getCurrentLimit();
$limit->day = $limits->getDayLimit();
......
......@@ -71,4 +71,9 @@ class APIRequest implements \App\Service\Contract\APIRequest {
{
throw new Exception('Я не знаю формата запрсов, чтобы его разбить');
}
function next($count)
{
throw new Exception('Я не знаю формата запрсов, чтобы его разбить');
}
}
......@@ -3,21 +3,51 @@ namespace App\Service\Requests;
use App\Models\Tokens;
/**
* Class DirectRequest
* @package App\Service\Requests
*
*
*/
class DirectRequest extends APIRequest {
CONST MAX_COUNT = 10000;
function chunk($count): ?\App\Service\Contract\APIRequest
{
if ($this->params['Page']['Limit'] <= $count) {
return null;
//для запросов get ничего не разбиваем, т.к. заранее не знаем сколько чего будет
//для них только после выполнения запроса будет известно есть ли кроме $count еще данные
//а текущий запрос просто устанавливается в $count
//а вот для запросов мутатрово будем разбивать. Но тут уже будет зависеть от запроса
//будем под каждый реализовывать делитель
if (!isset($this->params['Page'])){
$this->params['Page'] = [];
}
$nextCount = $this->params['Page']['Limit'] - $count;
$this->params['Page']['Limit'] = $count;
return null;
// if (!isset($this->params['Page'])){
// $this->params['Page']['Limit'] = $count;
// $this->params['Page']['Offset'] = 0;
// return null;
// }
// if ($this->params['Page']['Limit'] <= $count) {
// return null;
// }
//
// $this->params['Page']['Limit'] = $count;
//
// $new = clone $this;
// $params = $this->params;
// $params['Page']['Offset'] += $count;
// $new->setParams($params);
// return $new;
}
$new = clone $this;
$params = $this->params;
$params['Page']['Limit'] = $nextCount;
$new->setParams($params);
return $new;
function next($offset){
if (!isset($this->params['Page'])){
$this->params['Page'] = [];
}
$this->params['Page']['Limit'] = self::MAX_COUNT;
$this->params['Page']['Offset'] = $offset;
}
}
php artisan queue:work &
php artisan queue:work &
php artisan queue:work &
php artisan queue:work &
php artisan queue:work &
php artisan queue:work --queue=limits &
php artisan queue:work --queue=api &
php artisan queue:work --queue=api &
php artisan queue:work --queue=api &
php artisan queue:work --queue=api &
php artisan queue:work --queue=api &
php artisan schedule:work &
......@@ -61,7 +61,7 @@ class ApiRequestTest extends TestCase
]
]);
$request = $this->request->chunk(3);
$this->assertEquals($request->getParams()['Page']['Limit'], 7);
$this->assertEquals($request->getParams()['Page']['Offset'], 3);
$this->assertEquals($this->request->getParams()['Page']['Limit'], 3);
$this->request->setParams([
......
<?php
namespace Tests\Unit;
use App\Jobs\ProcessCallAPI;
use App\Jobs\ProcessCallLimitedAPI;
use App\Models\Account;
use App\Models\Tokens;
use App\Models\User;
use App\Service\Contract\API;
use App\Service\HeaderLimits;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Queue;
use Tests\TestCase;
class CallLimitedApiTest extends TestCase
{
use RefreshDatabase;
private $request;
private $token;
protected function setUp(): void
{
parent::setUp();
$this->request = \App\Service\Requests\APIRequest::getInstance(API::YANDEX);
$account = Account::create(['name' => 'Acme Corporation']);
$this->user = factory(User::class)->create([
'account_id' => $account->id,
'first_name' => 'John',
'last_name' => 'Doe',
'email' => 'johndoe@example.com',
'owner' => true,
]);
$this->token = factory(Tokens::class)->create([
'created_by' => $this->user->id
]);
}
public function testCall(){
Queue::fake();
Queue::assertNothingPushed();
$this->request->setToken($this->token);
$process = new ProcessCallLimitedAPI($this->request);
dispatch($process)->onQueue('limits');
Queue::assertPushed(ProcessCallLimitedAPI::class);
$process = new ProcessCallLimitedAPI($this->request);
dispatch($process)->onQueue('limits');
Queue::assertPushed(ProcessCallLimitedAPI::class, 2);
$this->token->limit=14;
$this->request->setService('AdGroups');
$this->request->setMethod('get');
$process->handle();
Queue::assertPushed(ProcessCallLimitedAPI::class, 3);
$this->token->limit=16;
$this->request->setService('AdGroups');
$this->request->setMethod('get');
$process->handle();
Queue::assertPushed(ProcessCallAPI::class);
$this->assertEquals($this->token->limit, 0);
}
}
......@@ -6,6 +6,7 @@ use App\Models\Account;
use App\Models\Tokens;
use App\Models\User;
use App\Service\Contract\API;
use App\Service\HeaderLimits;
use App\Service\Limits;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\Date;
......@@ -143,9 +144,32 @@ class LimitsTest extends TestCase
$this->assertEquals($objects, 4000);
}
public function testDoRezervNoLimit()
{
$request = \App\Service\Requests\APIRequest::getInstance(API::YANDEX);
$request->setService('AdExtensions');
$request->setMethod('add');
$this->expectException(\Exception::class);
$this->limitService->doRezerv($request, $this->token->limit);
}
public function testDoRezerv()
{
$this->markTestIncomplete();
$request = \App\Service\Requests\APIRequest::getInstance(API::YANDEX);
$request->setService('AdExtensions');
$request->setMethod('add');
$this->token->limit = 10;
$objects = $this->token->limit-5;
$last = $this->token->limit;
$id = $this->limitService->doRezerv($request, $objects);
$this->assertEquals($this->token->limit, 0);
$limit = \App\Models\Limits::find($id);
$this->assertEquals($limit->spent, $last);
$this->assertEquals(1, $limit->reserved);
$this->assertEquals($last, $limit->current);
}
public function testRemoveRezerv()
......@@ -162,10 +186,33 @@ class LimitsTest extends TestCase
}
public function testAcceptRezerv(){
$this->markTestIncomplete();
$limit = $this->token->limits()->save(factory(\App\Models\Limits::class)->make([
'reserved' => 1,
'spent' => 10
]));
$this->limitService->acceptRezerv(
$limit->id,
new HeaderLimits(['Units' => "1/2/3"])
);
$this->assertEquals($this->token->limit, 2);
$limit = \App\Models\Limits::find($limit->id);
$this->assertEquals($limit->spent, 1);
$this->assertEquals($limit->current, 2);
$this->assertEquals($limit->day, 3);
$this->assertEquals($limit->reserved, 0);
}
public function testUpdateLimits(){
$this->markTestIncomplete();
$this->limitService->updateLimits(
new HeaderLimits(['Units' => "1/2/3"])
);
$this->assertEquals($this->token->limit, 2);
$limit = $this->token->limits()->complited()->firstOrFail();
$this->assertEquals($limit->spent, 1);
$this->assertEquals($limit->current, 2);
$this->assertEquals($limit->day, 3);
$this->assertEquals($limit->reserved, 0);
}
}
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!