Skip to content

Commit 5a63495

Browse files
save progress
1 parent a8b86fa commit 5a63495

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2075
-289
lines changed

backend/app/Http/Controllers/UserController.php

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88
ChatGPT:
99
Here is a list of methods that the UserController class should have:
1010
11-
index(): This method should handle the GET /users endpoint, it should be responsible for fetching all the records from the "users" table and returning them to the client.
12-
13-
filter(): This method should handle the GET /users/filter?country=&from=&to= endpoint, it should be responsible for fetching all the records from the "users" table that match the provided country and date_of_birth range and returning them to the client.
11+
index(): This method should handle the GET /users?dateFrom=&dateTo=&country=&page=&perPage= endpoint, it should be responsible for fetching all the records from the "users" table that match the provided country and date_of_birth range and returning them to the client.
1412
Copilot: All parameters should be optional, if the client doesn't provide a country or date_of_birth range, the method should return all the users from the database.
1513
1614
show(): This method should handle the GET /users/{id} endpoint, it should be responsible for fetching a specific user from the "users" table and returning it to the client.
@@ -22,38 +20,43 @@
2220

2321
class UserController extends Controller
2422
{
25-
// GET /users - This endpoint is used to retrieve a list of all users from the database.
26-
public function index()
27-
{
28-
// Get all users from the database
29-
$users = \App\Models\User::all();
30-
// Return the users as a JSON
31-
return response()->json($users, 200);
32-
}
33-
34-
// GET /users/filter?country=&from=&to= - This endpoint is used to retrieve a list of all users from the database.
35-
public function filter(Request $request)
23+
// GET /users?dateFrom=&dateTo=&country=&page=&perPage= - This endpoint is used to retrieve a list of all users from the database.
24+
// dateFrom: nullable, unix timestamp, if provided, the users should be filtered by the date_of_birth field to only include users that were born after or on the provided date.
25+
// dateTo: nullable, unix timestamp, if provided, the users should be filtered by the date_of_birth field to only include users that were born before or on the provided date.
26+
// Laraver can't validate the dateFrom and dateTo parameters, so we have to do it manually by regexp
27+
public function index(Request $request)
3628
{
3729
// Validate the request
3830
$request->validate([
3931
'country' => 'exists:countries,id',
40-
'from' => 'date:Y-m-d|before_or_equal:to',
41-
'to' => 'date:Y-m-d|after_or_equal:from',
32+
'dateFrom' => 'regex:/^\d+$/|nullable',
33+
'dateTo' => 'regex:/^\d+$/|nullable',
34+
'page' => 'integer|min:1',
35+
'perPage' => 'integer|min:1',
4236
]);
43-
// Get the country and date_of_birth range from the request
44-
$country = $request->input('country');
45-
$from = $request->input('from');
46-
$to = $request->input('to');
37+
// assert that the all the parameters names are valid
38+
$validParams = ['country', 'dateFrom', 'dateTo', 'page', 'perPage'];
39+
$invalidParams = array_diff(array_keys($request->all()), $validParams);
40+
if (count($invalidParams) > 0) {
41+
return response()->json([
42+
'message' => 'Invalid parameters: ' . implode(', ', $invalidParams),
43+
], 400);
44+
}
45+
4746
// Filter the users based on the provided country and date_of_birth range
48-
$users = \App\Models\User::when($country, function ($query, $country) {
49-
return $query->where('country_id', $country);
50-
})->when($from, function ($query, $from) {
51-
return $query->where('date_of_birth', '>=', $from);
52-
})->when($to, function ($query, $to) {
53-
return $query->where('date_of_birth', '<=', $to);
54-
})->get();
47+
$usersQuery = \App\Models\User::query()
48+
->withCountryName()
49+
->dateRange($request->input('dateFrom'), $request->input('dateTo'))
50+
->country($request->input('country'));
51+
52+
$page = $request->input('page', 1);
53+
$perPage = $request->input('perPage', 10);
54+
$paginated = $usersQuery->paginate($perPage, ['*'], 'page', $page);
5555
// Return the users as a JSON
56-
return response()->json($users, 200);
56+
return response()->json([
57+
'users' => $paginated->items(),
58+
'totalPages' => (0 < $paginated->total()) ? $paginated->lastPage() : 0,
59+
], 200);
5760
}
5861

5962
// GET /users/{id} - This endpoint is used to retrieve a specific user from the database. The {id} path parameter should be replaced with the id of the user you want to retrieve.

backend/app/Models/User.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Illuminate\Database\Eloquent\Factories\HasFactory;
66
use Illuminate\Database\Eloquent\Model;
7+
use Illuminate\Database\Eloquent\Builder;
78

89
class User extends Model
910
{
@@ -22,4 +23,37 @@ class User extends Model
2223
public function country() {
2324
return $this->belongsTo(Country::class);
2425
}
26+
27+
public function scopeDateRange(Builder $query, $dateFrom = null, $dateTo = null)
28+
{
29+
if ($dateFrom && $dateTo) {
30+
return $query->whereBetween('date_of_birth', [date('Y-m-d', $dateFrom), date('Y-m-d', $dateTo)]);
31+
}
32+
33+
if ($dateFrom) {
34+
return $query->where('date_of_birth', '>=', date('Y-m-d', $dateFrom));
35+
}
36+
37+
if ($dateTo) {
38+
return $query->where('date_of_birth', '<=', date('Y-m-d', $dateTo));
39+
}
40+
41+
return $query;
42+
}
43+
44+
public function scopeCountry(Builder $query, $countryId = null)
45+
{
46+
if ($countryId) {
47+
return $query->where('country_id', $countryId);
48+
}
49+
50+
return $query;
51+
}
52+
53+
public function scopeWithCountryName($query)
54+
{
55+
$tbl = $this->getTable();
56+
return $query->leftJoin('countries', 'countries.id', '=', $tbl . '.country_id')
57+
->addSelect($tbl . '.*', 'countries.name as country_name');
58+
}
2559
}

backend/routes/api.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@
88
// POST /users - This endpoint is used to create a new user in the database. The client should send the user information in the request body.
99
Route::post('/users', 'UserController@store');
1010

11-
// GET /users/filter?country=&from=&to= - This endpoint is used to retrieve a list of all users from the database.
12-
Route::get('/users/filter', 'UserController@filter');
13-
1411
// GET /users/{id} - This endpoint is used to retrieve a specific user from the database. The {id} path parameter should be replaced with the id of the user you want to retrieve.
1512
Route::get('/users/{id}', 'UserController@show');
1613

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
<?php
2+
3+
namespace Tests\Unit\Routes;
4+
5+
class UsersFilterTest extends \Illuminate\Foundation\Testing\TestCase
6+
{
7+
use \Tests\CreatesApplication;
8+
use \Illuminate\Foundation\Testing\RefreshDatabase;
9+
10+
// return zero users and totalPages
11+
public function testNoUsers()
12+
{
13+
$response = $this->get('/api/users');
14+
$response->assertHeader('Content-Type', 'application/json');
15+
$response->assertJsonFragment([
16+
'users' => [],
17+
'totalPages' => 0,
18+
]);
19+
}
20+
21+
// Route '/api/users?country=&dateFrom=&dateTo=' should return a JSON with the filtered users
22+
public function testFilterUsers()
23+
{
24+
// Create a 2 countries
25+
$countries = \App\Models\Country::factory()->count(2)->create();
26+
// Create 3 users and assign them to the first country
27+
$users = \App\Models\User::factory()->count(3)->create([
28+
'country_id' => $countries[0]->id,
29+
'date_of_birth' => '1980-01-01',
30+
]);
31+
// Create a user with the country and date_of_birth that we want to filter
32+
$user = \App\Models\User::factory()->create([
33+
'country_id' => $countries[1]->id,
34+
'date_of_birth' => '1990-01-01',
35+
]);
36+
// Get the response from the route
37+
$response = $this->get('/api/users?country=' . $countries[1]->id);
38+
// Check that the response is a JSON
39+
$response->assertHeader('Content-Type', 'application/json');
40+
// Check that the response is contains the correct data for the filtered user
41+
$response->assertJsonFragment([
42+
'id' => $user->id,
43+
]);
44+
45+
// Check when filtering by date_of_birth
46+
$dateTimestamp = date_create_from_format('Y-m-d', '1990-01-01')->getTimestamp();
47+
$this->get('/api/users?dateFrom=' . $dateTimestamp)
48+
->assertJsonFragment([
49+
'id' => $user->id,
50+
]);
51+
$this->get('/api/users?dateTo=' . $dateTimestamp)
52+
->assertJsonFragment([
53+
'id' => $user->id,
54+
]);
55+
$this->get('/api/users?dateFrom=' . $dateTimestamp . '&dateTo=' . $dateTimestamp)
56+
->assertJsonFragment([
57+
'id' => $user->id,
58+
]);
59+
60+
// check inverse behavior
61+
$dateTimestamp = date_create_from_format('Y-m-d', '1990-01-02')->getTimestamp();
62+
$this->get('/api/users?dateFrom=' . $dateTimestamp)
63+
->assertJsonFragment([
64+
'users' => [],
65+
]);
66+
}
67+
68+
// This test should send a GET request to the /users?country=&dateFrom=&dateTo= endpoint with invalid or missing parameters and check if the response contains the appropriate error message or status code.
69+
public function testFilterUsersWithInvalidParameters()
70+
{
71+
// Create a country
72+
$country = \App\Models\Country::factory()->create();
73+
74+
// Get and check the response when the country parameter is invalid
75+
$this->get('/api/users?country=invalid&dateFrom=1&dateTo=0')
76+
->assertJsonFragment([
77+
'message' => 'The given data was invalid.',
78+
]);
79+
// Get and check the response when the from parameter is after the to parameter
80+
$this->get('/api/users?country=' . $country->id . '&dateTo=1980-01-01&dateFrom=1990-01-01')
81+
->assertJsonFragment([
82+
'message' => 'The given data was invalid.',
83+
]);
84+
// Get and check the response when the to parameter is before the from parameter
85+
$this->get('/api/users?country=' . $country->id . '&dateFrom=1990-01-01&dateTo=1980-01-01')
86+
->assertJsonFragment([
87+
'message' => 'The given data was invalid.',
88+
]);
89+
}
90+
91+
private function _setupUsers()
92+
{
93+
// Create dummy users
94+
$country = \App\Models\Country::factory()->create([
95+
'name' => 'Not USA',
96+
]);
97+
\App\Models\User::factory()->count(30)->create([
98+
'country_id' => $country->id,
99+
'date_of_birth' => '1980-01-01',
100+
]);
101+
// Create a good users with the country and date_of_birth that we want to filter
102+
$country = \App\Models\Country::factory()->create([
103+
'name' => 'USA',
104+
]);
105+
$users = \App\Models\User::factory()->count(30)->create([
106+
'country_id' => $country->id,
107+
'date_of_birth' => '1990-01-01',
108+
]);
109+
$query = [
110+
'country' => $country->id,
111+
'dateFrom' => date_create_from_format('Y-m-d', '1990-01-01')->getTimestamp(),
112+
'dateTo' => date_create_from_format('Y-m-d', '1990-01-01')->getTimestamp(),
113+
'page' => 1,
114+
'perPage' => 100000,
115+
];
116+
// Add the country_name to the users
117+
$users->each(function ($user) use ($country) {
118+
$user->country_name = $country->name;
119+
});
120+
return [$country, $users, $query];
121+
}
122+
123+
// should return a JSON with the filtered users with the default parameters
124+
public function testGetUsersWithDefaultParameters()
125+
{
126+
list($country, $users, $query) = $this->_setupUsers();
127+
// Check that the response is contains the correct data for the filtered user
128+
$this->get('/api/users?' . http_build_query($query))
129+
->assertJsonFragment([
130+
'users' => $users->toArray(),
131+
'totalPages' => 1,
132+
]);
133+
}
134+
135+
// should include the country_name in the response for each user
136+
public function testGetUsersWithCountryName()
137+
{
138+
list($country, $users, $query) = $this->_setupUsers();
139+
// Check that the response is contains the correct data for the filtered user
140+
$this->get('/api/users?' . http_build_query($query))
141+
->assertJsonFragment([
142+
'country_name' => $country->name,
143+
]);
144+
}
145+
146+
public function perPageProvider()
147+
{
148+
return [[5], [7], [13], [23],];
149+
}
150+
/**
151+
* @dataProvider perPageProvider
152+
*/
153+
public function testGetUsersAllPagesValid($perPage)
154+
{
155+
list($country, $users, $query) = $this->_setupUsers();
156+
$query['perPage'] = $perPage;
157+
$totalPages = ceil($users->count() / $perPage);
158+
for ($i = 1; $i <= $totalPages; $i++) {
159+
// Send the GET request with the current page
160+
$query['page'] = $i;
161+
$response = $this->get('/api/users?' . http_build_query($query));
162+
$expectedUsers = $users->slice(($i - 1) * $perPage, $perPage)->toArray();
163+
$response->assertJsonFragment([
164+
'users' => array_values($expectedUsers),
165+
'totalPages' => $totalPages,
166+
]);
167+
}
168+
}
169+
}

backend/tests/Unit/Routes/UsersTest.php

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@
1515
1616
testDeleteUser(): This test should send a DELETE request to the /users/{id} endpoint with a valid user id and check if the response is successful and if the user is deleted from the database. This test should be run after setting up some test data in the users table.
1717
18-
testFilterUsers(): This test should send a GET request to the /users/filter?country=&from=&to= endpoint with valid parameters for country and date_of_birth range and check if the response contains the filtered users. This test should be run after setting up some test data in the users and countries table.
19-
20-
testInvalidFilterParameters(): This test should send a GET request to the /users/filter?country=&from=&to= endpoint with invalid or missing parameters and check if the response contains the appropriate error message or status code.
21-
2218
testInvalidUpdateDeleteParameters(): This test should send a PATCH or DELETE request to the /users/{id} endpoint with invalid or missing parameters and check if the response contains the appropriate error message or status code.
2319
2420
testInvalidCreateParameters(): This test should send a POST request to the /users endpoint with invalid or missing parameters and check if the response contains the appropriate error message or status code.
@@ -147,60 +143,6 @@ public function testDeleteUser()
147143
]);
148144
}
149145

150-
// Route '/api/users/filter?country=&from=&to=' should return a JSON with the filtered users
151-
public function testFilterUsers()
152-
{
153-
// Create a 2 countries
154-
$countries = \App\Models\Country::factory()->count(2)->create();
155-
// Create 3 users and assign them to the first country
156-
$users = \App\Models\User::factory()->count(3)->create([
157-
'country_id' => $countries[0]->id,
158-
'date_of_birth' => '1980-01-01',
159-
]);
160-
// Create a user with the country and date_of_birth that we want to filter
161-
$user = \App\Models\User::factory()->create([
162-
'country_id' => $countries[1]->id,
163-
'date_of_birth' => '1990-01-01',
164-
]);
165-
// Get the response from the route
166-
$response = $this->get('/api/users/filter?country=' . $countries[1]->id);
167-
// Check that the response is a JSON
168-
$response->assertHeader('Content-Type', 'application/json');
169-
// Check that the response is contains the correct data for the filtered user
170-
$response->assertJsonFragment([
171-
'id' => $user->id,
172-
]);
173-
174-
// Check when filtering by date_of_birth
175-
$this->get('/api/users/filter?from=1990-01-01&to=1990-01-01')
176-
->assertJsonFragment([
177-
'id' => $user->id,
178-
]);
179-
}
180-
181-
// This test should send a GET request to the /users/filter?country=&from=&to= endpoint with invalid or missing parameters and check if the response contains the appropriate error message or status code.
182-
public function testFilterUsersWithInvalidParameters()
183-
{
184-
// Create a country
185-
$country = \App\Models\Country::factory()->create();
186-
187-
// Get and check the response when the country parameter is invalid
188-
$this->get('/api/users/filter?country=invalid&from=1980-01-01&to=1990-01-01')
189-
->assertJsonFragment([
190-
'message' => 'The given data was invalid.',
191-
]);
192-
// Get and check the response when the from parameter is after the to parameter
193-
$this->get('/api/users/filter?country=' . $country->id . '&to=1980-01-01&from=1990-01-01')
194-
->assertJsonFragment([
195-
'message' => 'The given data was invalid.',
196-
]);
197-
// Get and check the response when the to parameter is before the from parameter
198-
$this->get('/api/users/filter?country=' . $country->id . '&from=1990-01-01&to=1980-01-01')
199-
->assertJsonFragment([
200-
'message' => 'The given data was invalid.',
201-
]);
202-
}
203-
204146
// testInvalidUpdateParameters(): This test should send a PATCH request to the /users/{id} endpoint with invalid or missing parameters and check if the response contains the appropriate error message or status code.
205147
function testInvalidUpdateParameters()
206148
{

frontend/.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
REACT_APP_API_URL=/api

frontend/.env.development

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
REACT_APP_API_URL=http://localhost:8000/api

0 commit comments

Comments
 (0)