Skip to content
This repository was archived by the owner on Jan 1, 2021. It is now read-only.

Commit 2d9415b

Browse files
committed
Better handle 429 exceptions and increase rate limiting.
1 parent 959e9d4 commit 2d9415b

5 files changed

Lines changed: 120 additions & 26 deletions

File tree

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
/**
3+
* Created by PhpStorm.
4+
* User: leonardogalli
5+
* Date: 13.08.17
6+
* Time: 17:42
7+
*/
8+
9+
namespace App\Exceptions;
10+
use Carbon\Carbon;
11+
use Symfony\Component\HttpKernel\Exception\HttpException;
12+
13+
class TooManyRequestsHttpException extends HttpException
14+
{
15+
/**
16+
* Constructor.
17+
*
18+
* @param int|string $retryAfter The number of seconds or HTTP-date after which the request may be retried
19+
* @param string $message The internal exception message
20+
* @param \Exception $previous The previous exception
21+
* @param int $code The internal exception code
22+
*/
23+
public function __construct($retryAfter = null, $maxAttempts = null, \Exception $previous = null, $code = 0)
24+
{
25+
$headers = array();
26+
$message = "You have sent too many requests.";
27+
28+
if ($retryAfter) {
29+
$headers = array('Retry-After' => $retryAfter);
30+
$headers['X-RateLimit-Reset'] = Carbon::now()->getTimestamp() + $retryAfter;
31+
$message .= " Please retry after $retryAfter seconds.";
32+
}
33+
34+
if ($maxAttempts) {
35+
$headers['X-RateLimit-Limit'] = $maxAttempts;
36+
$headers['X-RateLimit-Remaining'] = 0;
37+
}
38+
39+
parent::__construct(429, $message, $previous, $headers, $code);
40+
}
41+
}

app/Http/Controllers/API/DiscoverController.php

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use App\Http\Controllers\Controller;
1111
use Illuminate\Support\Facades\Cache;
1212
use Illuminate\Support\Facades\DB;
13+
use Illuminate\Support\Facades\Input;
1314
use Symfony\Component\HttpFoundation\Response;
1415
use Symfony\Component\HttpFoundation\JsonResponse;
1516
use Symfony\Component\HttpFoundation\Request;
@@ -30,13 +31,15 @@ public function upcoming() {
3031
$query->whereIn("type", array(4,5,6))->whereBetween("release_date", array(Carbon::now()->subWeek(), Carbon::now()->addWeeks(3)))->orderBy("release_date", "ASC");
3132
})->where("adult", "=", "0")->orderBy("popularity", "DESC")->get()->toArray();
3233
});
34+
$resp = $this->filterMovies($resp);
3335
return response()->json($resp);
3436
}
3537

3638
public function popular() {
3739
$movies = Cache::remember("discovery.popular", new Carbon('tomorrow midnight'), function(){
3840
return array_values(StevenLuMovie::all()->sortByDesc("TMDBMovie.popularity")->toArray());
3941
});
42+
$movies = $this->filterMovies($movies);
4043
return response()->json($movies);
4144
}
4245

@@ -48,36 +51,58 @@ public function popular() {
4851
public function recommendations(Request $request) {
4952
$ids = $request->input("tmdbIds");
5053
$ignoredIds = $request->input("ignoredIds");
51-
if ($ignoredIds != "")
52-
{
53-
$ignoredIds = ",".$ignoredIds;
54-
}
54+
$movies = Cache::remember("discovery.recommendations.$ids.$ignoredIds", new Carbon("tomorrow midnight"), function() use($ignoredIds, $ids){
55+
if ($ignoredIds != "")
56+
{
57+
$ignoredIds = ",".$ignoredIds;
58+
}
5559

56-
if ($ids == "" || $ids == null)
57-
{
58-
abort(422, "Please add some movies before using our recommendation engine :)");
59-
}
60-
$movies_db = DB::select("SELECT mo.id, mo.popularity, mo.imdb_id, mo.title, mo.overview, mo.vote_average, mo.vote_count, mo.tagline, mo.poster_path, mo.release_date, mo.release_year, mo.trailer_key, mo.trailer_site, mo.backdrop_path, mo.homepage, mo.runtime, mo.countO, mo.genres, mo.runtime, mo.adult FROM ( SELECT m.*, r.recommended_id, r.tmdbid, r.id as rid, count(m.id) as countO FROM movies m, recommendations r WHERE m.id = r.recommended_id AND r.tmdbid in ($ids) AND r.recommended_id not in ($ids$ignoredIds) AND m.adult = 0 GROUP BY m.id ) as mo;");
61-
$movies = json_decode(json_encode($movies_db), true);
60+
if ($ids == "" || $ids == null)
61+
{
62+
abort(422, "Please add some movies before using our recommendation engine :)");
63+
}
64+
$movies_db = DB::select("SELECT mo.id, mo.popularity, mo.imdb_id, mo.title, mo.overview, mo.vote_average, mo.vote_count, mo.tagline, mo.poster_path, mo.release_date, mo.release_year, mo.trailer_key, mo.trailer_site, mo.backdrop_path, mo.homepage, mo.runtime, mo.countO, mo.genres, mo.runtime, mo.adult FROM ( SELECT m.*, r.recommended_id, r.tmdbid, r.id as rid, count(m.id) as countO FROM movies m, recommendations r WHERE m.id = r.recommended_id AND r.tmdbid in ($ids) AND r.recommended_id not in ($ids$ignoredIds) AND m.adult = 0 GROUP BY m.id ) as mo;");
65+
$movies = json_decode(json_encode($movies_db), true);
6266

63-
$this->count_max = maximum($movies, "countO");
64-
$this->pop_max = maximum($movies, "popularity");
65-
$this->vote_max = maximum($movies, "vote_average");
67+
$this->count_max = maximum($movies, "countO");
68+
$this->pop_max = maximum($movies, "popularity");
69+
$this->vote_max = maximum($movies, "vote_average");
6670

67-
usort($movies, array($this, "compare_score"));
71+
usort($movies, array($this, "compare_score"));
6872

69-
$movies = array_slice($movies, 0, 30);
70-
$resp = [];
73+
$movies = array_slice($movies, 0, 30);
74+
$resp = [];
7175

72-
foreach ($movies as $movie) {
73-
unset($movie["countO"]);
74-
$movie["genres"] = explode(",", $movie["genres"]);
75-
$movie["adult"] = $movie["adult"] == 1;
76-
$resp[] = $movie;
77-
}
76+
foreach ($movies as $movie) {
77+
unset($movie["countO"]);
78+
$movie["genres"] = explode(",", $movie["genres"]);
79+
$movie["adult"] = $movie["adult"] == 1;
80+
$resp[] = $movie;
81+
}
7882

83+
return $resp;
84+
});
7985

80-
return response()->json($resp);
86+
$movies = $this->filterMovies($movies);
87+
88+
dd($movies);
89+
90+
return response()->json($movies);
91+
}
92+
93+
function filterMovies($movies)
94+
{
95+
$yearLower = Input::get('yearLower', 1800);
96+
$yearUpper = Input::get('yearUpper', 2300);
97+
$genreIds = Input::get('genreIds', "");
98+
if (!(is_array($genreIds) || $genreIds === null))
99+
{
100+
$genreIds = explode(",", $genreIds);
101+
}
102+
return array_filter($movies, function($value) use ($yearLower, $yearUpper, $genreIds) {
103+
//dd($genreIds);
104+
return $value["release_year"] >= $yearLower && $value["release_year"] <= $yearUpper && ($genreIds != array("") ? count(array_intersect($value["genres"], $genreIds)) > 0 : true);
105+
});
81106
}
82107

83108
function score ($elem)

app/Http/Kernel.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class Kernel extends HttpKernel
3737
],
3838

3939
'api' => [
40-
'throttle:60,1',
40+
'throttle:240,1',
4141
'bindings',
4242
\App\Http\Middleware\CheckDBMaintAPI::class,
4343
],
@@ -56,6 +56,6 @@ class Kernel extends HttpKernel
5656
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
5757
'can' => \Illuminate\Auth\Middleware\Authorize::class,
5858
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
59-
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
59+
'throttle' => \App\Http\Middleware\ThrottleRequestsException::class,
6060
];
6161
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
/**
3+
* Created by PhpStorm.
4+
* User: leonardogalli
5+
* Date: 13.08.17
6+
* Time: 17:34
7+
*/
8+
9+
namespace App\Http\Middleware;
10+
11+
use App\Exceptions\TooManyRequestsHttpException;
12+
use Illuminate\Routing\Middleware\ThrottleRequests;
13+
14+
class ThrottleRequestsException extends ThrottleRequests
15+
{
16+
/**
17+
* Create a 'too many attempts' response.
18+
*
19+
* @param string $key
20+
* @param int $maxAttempts
21+
* @return \Symfony\Component\HttpFoundation\Response
22+
*/
23+
protected function buildResponse($key, $maxAttempts)
24+
{
25+
$retryAfter = $this->limiter->availableIn($key);
26+
throw new TooManyRequestsHttpException($retryAfter, $maxAttempts);
27+
}
28+
}

config/exceptions.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@
103103
'Illuminate\Database\Eloquent\ModelNotFoundException' => 'warning',
104104
'Illuminate\Session\TokenMismatchException' => 'notice',
105105
'Symfony\Component\HttpKernel\Exception\NotFoundHttpException' => 'notice',
106-
'Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException' => 'error',
106+
'Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException' => 'warning',
107107
'Symfony\Component\HttpKernel\Exception\HttpExceptionInterface' => 'warning',
108108
'Symfony\Component\Debug\Exception\FatalErrorException' => 'critical',
109109
'Exception' => 'error',

0 commit comments

Comments
 (0)