Skip to content
Open
3 changes: 0 additions & 3 deletions app/Http/Controllers/ProfilesApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,6 @@ public function index(ProfilesApiRequest $request): JsonResponse
}

if ($request->boolean('with_data')) {
if(count(array_filter($request->query())) <=1){
return response()->json(['error' => 'Please use a filter when pulling data.'], 400);
}
$profile = $profile->withApiData($request->input('data_type'));
}

Expand Down
50 changes: 41 additions & 9 deletions app/Http/Requests/ProfilesApiRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@

namespace App\Http\Requests;

use App\Profile;
use App\Rules\AllowedProfileDataType;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;

class ProfilesApiRequest extends FormRequest
{
Expand All @@ -27,14 +26,22 @@ public function authorize()
public function rules()
{
return [
'person' => 'sometimes|string',
'search' => 'sometimes|string',
'search_names' => 'sometimes|string',
'info_contains' => 'sometimes|string',
'from_school' => 'sometimes|string',
'tag' => 'sometimes|string',
'person' => ['sometimes', 'string', 'regex:/^[a-zA-Z0-9.;]+$/'],
'search' => ['sometimes', 'string', 'regex:/^[a-zA-Z0-9\s,\.]*$/', 'min:3'],
'search_names' => ['sometimes', 'string', 'regex:/^[a-zA-Z0-9\s,\.]*$/', 'min:3'],
'info_contains' => ['sometimes', 'string', 'regex:/^[a-zA-Z0-9\s,\.]*$/', 'min:3'],
'from_school' => [
'sometimes',
'string',
'regex:/^[a-zA-Z0-9\s;,\.]+$/',
],
'tag' => ['sometimes', 'string', 'alpha_num', 'min:3'],
'public' => 'sometimes|boolean',
'with_data' => 'sometimes|boolean',
'with_data' => [
'sometimes',
'boolean',
$this->validateWithData(),
],
'raw_data' => 'sometimes|boolean',
'data_type' => [
'sometimes',
Expand All @@ -43,4 +50,29 @@ public function rules()
],
];
}

public function validateWithData()
{
return function ($attribute, $value, $fail) {
if ($value && empty($this->person)) {
$fail('Invalid parameter.');
}
};
}

protected function passedValidation(): void
{
$allowedKeys = array_keys($this->rules());

$extraKeys = collect($this->all())
->keys()
->diff($allowedKeys);

if ($extraKeys->isNotEmpty()) {
throw ValidationException::withMessages([
'extra_parameters' => 'Invalid parameter.',
]);
}
}

}
27 changes: 27 additions & 0 deletions app/Rules/AllowedSchools.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace App\Rules;

use App\School;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class AllowedSchools implements ValidationRule
{
/**
* Run the validation rule.
*
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$allowed_schools = School::where('short_name', '!=', 'Other')
->pluck('short_name')
->toArray();

if(!in_array($value, $allowed_schools)){
$fail("Invalid value for school.");
}

}
}
119 changes: 106 additions & 13 deletions tests/Feature/ApiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,6 @@ public function testApi()
$response->assertJsonFragment($this->profileJsonFragment($profiles[$i]));
}

////////////////////////////
// All profiles with data //
////////////////////////////

// profiles.test/api/v1?with_data=1
$response = $this->get(route('api.index', ['with_data' => 1]));

$response
->assertStatus(400)
->assertJson([
'error' => "Please use a filter when pulling data.",
]);

////////////////////////////////
// Certain profiles with data //
////////////////////////////////
Expand Down Expand Up @@ -117,6 +104,112 @@ public function testApi()
->assertJsonFragment($this->profileInfoJsonFragment($profiles[0]->data->first()));
}

/**
* Test validation fails for profiles with data when 'person' is missing
*
* Endpoint: profiles.test/api/v1?with_data=1
*/
public function testWithDataFails()
{
$response = $this->get(route('api.index', ['with_data' => 1]));

$response
->assertStatus(422)
->assertJsonValidationErrors('with_data')
->assertJson([
'errors' => [
'with_data' => ["Invalid parameter."],
],
]);
}

/**
* Test validation fails for 'person' that includes an invalid character
*
* Endpoint: profiles.test/api/v1?person=invalid#char
*/
public function testPersonFails()
{
$response = $this->get(route('api.index', ['person' => 'invalid#char']));

$response
->assertStatus(422)
->assertJsonValidationErrors('person');
}

/**
* Test validation fails for an invalid profile data type
*
* Endpoint: profiles.test/api/v1?data_type=invalid_type
*/
public function testDataTypeFails()
{
$response = $this->get(route('api.index', ['data_type' => 'invalid_type']));

$response
->assertStatus(422)
->assertJsonValidationErrors('data_type');
}

/**
* Test validation fails for an invalid value for from_school
*
* Endpoint: profiles.test/api/v1?from_school=Other
*/
public function testSchoolFails()
{
$response = $this->get(route('api.index', ['from_school' => 'Other:']));

$response
->assertStatus(422)
->assertJsonValidationErrors('from_school');
}

/**
* Test validation fails for:
* Non-alphanumeric characters in 'search'
* 'search_names' values shorter than 3 characters
* Non-string values in 'info_contains'
*/
public function testSearchFails()
{
$parameters = [
'search' => 'invalid_search_entry!',
'search_names' => 'No',
'info_contains' => ['not', 'a', 'string'],
];

$response = $this->get(route('api.index', $parameters));

$response
->assertStatus(422)
->assertJsonValidationErrors('search')
->assertJson([
'errors' => [
'search' => ["The search format is invalid."],
],
]);

$response
->assertJsonValidationErrors('search_names')
->assertJson([
'errors' => [
'search_names' => ["The search names must be at least 3 characters."],
],
]);

$response
->assertJsonValidationErrors('info_contains')
->assertJson([
'errors' => [
'info_contains' => [
"The info contains must be a string.",
"The info contains format is invalid.",
],
],
]);
}

/**
* Get the proper Profile JSON fragment
*
Expand Down
Loading