Skip to content

Commit

Permalink
feat: #4 params validation
Browse files Browse the repository at this point in the history
  • Loading branch information
sinkcup committed Oct 23, 2021
1 parent 49fce56 commit 984523c
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 4 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"require": {
"php": ">=7.4",
"ext-json": "*",
"guzzlehttp/guzzle": "^7.4"
"guzzlehttp/guzzle": "^7.4",
"illuminate/validation": "^8.67"
},
"require-dev": {
"fakerphp/faker": "^1.16",
Expand Down
158 changes: 158 additions & 0 deletions lang/en/validation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<?php

return [

/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here.
|
*/

'accepted' => 'The :attribute must be accepted.',
'accepted_if' => 'The :attribute must be accepted when :other is :value.',
'active_url' => 'The :attribute is not a valid URL.',
'after' => 'The :attribute must be a date after :date.',
'after_or_equal' => 'The :attribute must be a date after or equal to :date.',
'alpha' => 'The :attribute must only contain letters.',
'alpha_dash' => 'The :attribute must only contain letters, numbers, dashes and underscores.',
'alpha_num' => 'The :attribute must only contain letters and numbers.',
'array' => 'The :attribute must be an array.',
'before' => 'The :attribute must be a date before :date.',
'before_or_equal' => 'The :attribute must be a date before or equal to :date.',
'between' => [
'numeric' => 'The :attribute must be between :min and :max.',
'file' => 'The :attribute must be between :min and :max kilobytes.',
'string' => 'The :attribute must be between :min and :max characters.',
'array' => 'The :attribute must have between :min and :max items.',
],
'boolean' => 'The :attribute field must be true or false.',
'confirmed' => 'The :attribute confirmation does not match.',
'current_password' => 'The password is incorrect.',
'date' => 'The :attribute is not a valid date.',
'date_equals' => 'The :attribute must be a date equal to :date.',
'date_format' => 'The :attribute does not match the format :format.',
'different' => 'The :attribute and :other must be different.',
'digits' => 'The :attribute must be :digits digits.',
'digits_between' => 'The :attribute must be between :min and :max digits.',
'dimensions' => 'The :attribute has invalid image dimensions.',
'distinct' => 'The :attribute field has a duplicate value.',
'email' => 'The :attribute must be a valid email address.',
'ends_with' => 'The :attribute must end with one of the following: :values.',
'exists' => 'The selected :attribute is invalid.',
'file' => 'The :attribute must be a file.',
'filled' => 'The :attribute field must have a value.',
'gt' => [
'numeric' => 'The :attribute must be greater than :value.',
'file' => 'The :attribute must be greater than :value kilobytes.',
'string' => 'The :attribute must be greater than :value characters.',
'array' => 'The :attribute must have more than :value items.',
],
'gte' => [
'numeric' => 'The :attribute must be greater than or equal to :value.',
'file' => 'The :attribute must be greater than or equal to :value kilobytes.',
'string' => 'The :attribute must be greater than or equal to :value characters.',
'array' => 'The :attribute must have :value items or more.',
],
'image' => 'The :attribute must be an image.',
'in' => 'The selected :attribute is invalid.',
'in_array' => 'The :attribute field does not exist in :other.',
'integer' => 'The :attribute must be an integer.',
'ip' => 'The :attribute must be a valid IP address.',
'ipv4' => 'The :attribute must be a valid IPv4 address.',
'ipv6' => 'The :attribute must be a valid IPv6 address.',
'json' => 'The :attribute must be a valid JSON string.',
'lt' => [
'numeric' => 'The :attribute must be less than :value.',
'file' => 'The :attribute must be less than :value kilobytes.',
'string' => 'The :attribute must be less than :value characters.',
'array' => 'The :attribute must have less than :value items.',
],
'lte' => [
'numeric' => 'The :attribute must be less than or equal to :value.',
'file' => 'The :attribute must be less than or equal to :value kilobytes.',
'string' => 'The :attribute must be less than or equal to :value characters.',
'array' => 'The :attribute must not have more than :value items.',
],
'max' => [
'numeric' => 'The :attribute must not be greater than :max.',
'file' => 'The :attribute must not be greater than :max kilobytes.',
'string' => 'The :attribute must not be greater than :max characters.',
'array' => 'The :attribute must not have more than :max items.',
],
'mimes' => 'The :attribute must be a file of type: :values.',
'mimetypes' => 'The :attribute must be a file of type: :values.',
'min' => [
'numeric' => 'The :attribute must be at least :min.',
'file' => 'The :attribute must be at least :min kilobytes.',
'string' => 'The :attribute must be at least :min characters.',
'array' => 'The :attribute must have at least :min items.',
],
'multiple_of' => 'The :attribute must be a multiple of :value.',
'not_in' => 'The selected :attribute is invalid.',
'not_regex' => 'The :attribute format is invalid.',
'numeric' => 'The :attribute must be a number.',
'password' => 'The password is incorrect.',
'present' => 'The :attribute field must be present.',
'regex' => 'The :attribute format is invalid.',
'required' => 'The :attribute field is required.',
'required_if' => 'The :attribute field is required when :other is :value.',
'required_unless' => 'The :attribute field is required unless :other is in :values.',
'required_with' => 'The :attribute field is required when :values is present.',
'required_with_all' => 'The :attribute field is required when :values are present.',
'required_without' => 'The :attribute field is required when :values is not present.',
'required_without_all' => 'The :attribute field is required when none of :values are present.',
'prohibited' => 'The :attribute field is prohibited.',
'prohibited_if' => 'The :attribute field is prohibited when :other is :value.',
'prohibited_unless' => 'The :attribute field is prohibited unless :other is in :values.',
'prohibits' => 'The :attribute field prohibits :other from being present.',
'same' => 'The :attribute and :other must match.',
'size' => [
'numeric' => 'The :attribute must be :size.',
'file' => 'The :attribute must be :size kilobytes.',
'string' => 'The :attribute must be :size characters.',
'array' => 'The :attribute must contain :size items.',
],
'starts_with' => 'The :attribute must start with one of the following: :values.',
'string' => 'The :attribute must be a string.',
'timezone' => 'The :attribute must be a valid timezone.',
'unique' => 'The :attribute has already been taken.',
'uploaded' => 'The :attribute failed to upload.',
'url' => 'The :attribute must be a valid URL.',
'uuid' => 'The :attribute must be a valid UUID.',

/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/

'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],

/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap our attribute placeholder
| with something more reader friendly such as "E-Mail Address" instead
| of "email". This simply helps us make our message more expressive.
|
*/

'attributes' => [],

];
1 change: 1 addition & 0 deletions src/Core.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Coding;

use Coding\Exceptions\ApiError;
use GuzzleHttp\Client;

class Core
Expand Down
2 changes: 1 addition & 1 deletion src/ApiError.php → src/Exceptions/ApiError.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Coding;
namespace Coding\Exceptions;

use Exception;

Expand Down
9 changes: 9 additions & 0 deletions src/Exceptions/ValidationException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Coding\Exceptions;

use Exception;

class ValidationException extends Exception
{
}
22 changes: 22 additions & 0 deletions src/Handlers/Validator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Coding\Handlers;

use Illuminate\Filesystem\Filesystem;
use Illuminate\Translation\FileLoader;
use Illuminate\Translation\Translator;
use Illuminate\Validation\Factory;

class Validator extends Factory
{
public static function getInstance(): Factory
{
static $validator = null;
if ($validator === null) {
$fileLoader = new FileLoader(new Filesystem(), __DIR__ . '/../../lang');
$translator = new Translator($fileLoader, 'en');
$validator = new Factory($translator);
}
return $validator;
}
}
15 changes: 15 additions & 0 deletions src/Iteration.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace Coding;

use Coding\Exceptions\ValidationException;
use Coding\Handlers\Validator;

class Iteration
{
private Core $core;
Expand All @@ -13,6 +16,18 @@ public function __construct(string $token, Core $core = null)

public function create(array $data)
{
$validator = Validator::getInstance()->make($data, [
'ProjectName' => 'string|required',
'Name' => 'string|required',
'Goal' => 'string',
'Assignee' => 'integer',
'StartAt' => 'date_format:Y-m-d',
'EndAt' => 'date_format:Y-m-d|after:StartAt',
]);
if ($validator->fails()) {
// TODO Laravel ValidationException no message
throw new ValidationException($validator->errors()->all()[0]);
}
$response = $this->core->request('CreateIteration', $data);
return $response['Iteration'];
}
Expand Down
2 changes: 1 addition & 1 deletion tests/CoreTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Coding\Tests;

use Coding\ApiError;
use Coding\Exceptions\ApiError;
use Coding\Core;
use GuzzleHttp\Psr7\Response;

Expand Down
57 changes: 56 additions & 1 deletion tests/IterationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
namespace Coding\Tests;

use Coding\Core;
use Coding\Exceptions\ValidationException;
use Coding\Iteration;

class IterationTest extends TestCase
{
public function testCreateSuccess()
public function testCreateSuccessWithOnlyRequiredParams()
{
$coreMock = \Mockery::mock(Core::class, [])->makePartial();

Expand All @@ -28,4 +29,58 @@ public function testCreateSuccess()
$result = $iteration->create($data);
$this->assertEquals($response['Iteration'], $result);
}

public function testCreateSuccessWithAllParams()
{
$coreMock = \Mockery::mock(Core::class, [])->makePartial();

$response = json_decode(
file_get_contents($this->dataPath('CreateIterationResponse.json')),
true
)['Response'];
$startAt = $this->faker->date();
$data = [
'ProjectName' => $this->projectName,
'Name' => $this->faker->title,
'Goal' => $this->faker->sentence,
'Assignee' => $this->faker->randomNumber(),
'StartAt' => $startAt,
'EndAt' => date('Y-m-d', strtotime($startAt) + $this->faker->randomDigitNotZero() * 86400),
];
$coreMock->shouldReceive('request')->times(1)->withArgs([
'CreateIteration',
$data
])->andReturn($response);

$iteration = new Iteration($this->token, $coreMock);
$result = $iteration->create($data);
$this->assertEquals($response['Iteration'], $result);
}

public function testCreateFailedRequired()
{
$data = [
'ProjectName' => $this->projectName,
];

$iteration = new Iteration($this->token);
$this->expectException(ValidationException::class);
$this->expectExceptionMessage('The name field is required.');
$iteration->create($data);
}

public function testCreateFailedBefore()
{
$data = [
'ProjectName' => $this->projectName,
'Name' => $this->faker->title,
'StartAt' => '2021-10-23',
'EndAt' => '2021-10-22',
];

$iteration = new Iteration($this->token);
$this->expectException(ValidationException::class);
$this->expectExceptionMessage('The end at must be a date after start at.');
$iteration->create($data);
}
}

0 comments on commit 984523c

Please sign in to comment.