Skip to content

Commit 984523c

Browse files
committed
feat: #4 params validation
1 parent 49fce56 commit 984523c

9 files changed

+265
-4
lines changed

composer.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"require": {
66
"php": ">=7.4",
77
"ext-json": "*",
8-
"guzzlehttp/guzzle": "^7.4"
8+
"guzzlehttp/guzzle": "^7.4",
9+
"illuminate/validation": "^8.67"
910
},
1011
"require-dev": {
1112
"fakerphp/faker": "^1.16",

lang/en/validation.php

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
<?php
2+
3+
return [
4+
5+
/*
6+
|--------------------------------------------------------------------------
7+
| Validation Language Lines
8+
|--------------------------------------------------------------------------
9+
|
10+
| The following language lines contain the default error messages used by
11+
| the validator class. Some of these rules have multiple versions such
12+
| as the size rules. Feel free to tweak each of these messages here.
13+
|
14+
*/
15+
16+
'accepted' => 'The :attribute must be accepted.',
17+
'accepted_if' => 'The :attribute must be accepted when :other is :value.',
18+
'active_url' => 'The :attribute is not a valid URL.',
19+
'after' => 'The :attribute must be a date after :date.',
20+
'after_or_equal' => 'The :attribute must be a date after or equal to :date.',
21+
'alpha' => 'The :attribute must only contain letters.',
22+
'alpha_dash' => 'The :attribute must only contain letters, numbers, dashes and underscores.',
23+
'alpha_num' => 'The :attribute must only contain letters and numbers.',
24+
'array' => 'The :attribute must be an array.',
25+
'before' => 'The :attribute must be a date before :date.',
26+
'before_or_equal' => 'The :attribute must be a date before or equal to :date.',
27+
'between' => [
28+
'numeric' => 'The :attribute must be between :min and :max.',
29+
'file' => 'The :attribute must be between :min and :max kilobytes.',
30+
'string' => 'The :attribute must be between :min and :max characters.',
31+
'array' => 'The :attribute must have between :min and :max items.',
32+
],
33+
'boolean' => 'The :attribute field must be true or false.',
34+
'confirmed' => 'The :attribute confirmation does not match.',
35+
'current_password' => 'The password is incorrect.',
36+
'date' => 'The :attribute is not a valid date.',
37+
'date_equals' => 'The :attribute must be a date equal to :date.',
38+
'date_format' => 'The :attribute does not match the format :format.',
39+
'different' => 'The :attribute and :other must be different.',
40+
'digits' => 'The :attribute must be :digits digits.',
41+
'digits_between' => 'The :attribute must be between :min and :max digits.',
42+
'dimensions' => 'The :attribute has invalid image dimensions.',
43+
'distinct' => 'The :attribute field has a duplicate value.',
44+
'email' => 'The :attribute must be a valid email address.',
45+
'ends_with' => 'The :attribute must end with one of the following: :values.',
46+
'exists' => 'The selected :attribute is invalid.',
47+
'file' => 'The :attribute must be a file.',
48+
'filled' => 'The :attribute field must have a value.',
49+
'gt' => [
50+
'numeric' => 'The :attribute must be greater than :value.',
51+
'file' => 'The :attribute must be greater than :value kilobytes.',
52+
'string' => 'The :attribute must be greater than :value characters.',
53+
'array' => 'The :attribute must have more than :value items.',
54+
],
55+
'gte' => [
56+
'numeric' => 'The :attribute must be greater than or equal to :value.',
57+
'file' => 'The :attribute must be greater than or equal to :value kilobytes.',
58+
'string' => 'The :attribute must be greater than or equal to :value characters.',
59+
'array' => 'The :attribute must have :value items or more.',
60+
],
61+
'image' => 'The :attribute must be an image.',
62+
'in' => 'The selected :attribute is invalid.',
63+
'in_array' => 'The :attribute field does not exist in :other.',
64+
'integer' => 'The :attribute must be an integer.',
65+
'ip' => 'The :attribute must be a valid IP address.',
66+
'ipv4' => 'The :attribute must be a valid IPv4 address.',
67+
'ipv6' => 'The :attribute must be a valid IPv6 address.',
68+
'json' => 'The :attribute must be a valid JSON string.',
69+
'lt' => [
70+
'numeric' => 'The :attribute must be less than :value.',
71+
'file' => 'The :attribute must be less than :value kilobytes.',
72+
'string' => 'The :attribute must be less than :value characters.',
73+
'array' => 'The :attribute must have less than :value items.',
74+
],
75+
'lte' => [
76+
'numeric' => 'The :attribute must be less than or equal to :value.',
77+
'file' => 'The :attribute must be less than or equal to :value kilobytes.',
78+
'string' => 'The :attribute must be less than or equal to :value characters.',
79+
'array' => 'The :attribute must not have more than :value items.',
80+
],
81+
'max' => [
82+
'numeric' => 'The :attribute must not be greater than :max.',
83+
'file' => 'The :attribute must not be greater than :max kilobytes.',
84+
'string' => 'The :attribute must not be greater than :max characters.',
85+
'array' => 'The :attribute must not have more than :max items.',
86+
],
87+
'mimes' => 'The :attribute must be a file of type: :values.',
88+
'mimetypes' => 'The :attribute must be a file of type: :values.',
89+
'min' => [
90+
'numeric' => 'The :attribute must be at least :min.',
91+
'file' => 'The :attribute must be at least :min kilobytes.',
92+
'string' => 'The :attribute must be at least :min characters.',
93+
'array' => 'The :attribute must have at least :min items.',
94+
],
95+
'multiple_of' => 'The :attribute must be a multiple of :value.',
96+
'not_in' => 'The selected :attribute is invalid.',
97+
'not_regex' => 'The :attribute format is invalid.',
98+
'numeric' => 'The :attribute must be a number.',
99+
'password' => 'The password is incorrect.',
100+
'present' => 'The :attribute field must be present.',
101+
'regex' => 'The :attribute format is invalid.',
102+
'required' => 'The :attribute field is required.',
103+
'required_if' => 'The :attribute field is required when :other is :value.',
104+
'required_unless' => 'The :attribute field is required unless :other is in :values.',
105+
'required_with' => 'The :attribute field is required when :values is present.',
106+
'required_with_all' => 'The :attribute field is required when :values are present.',
107+
'required_without' => 'The :attribute field is required when :values is not present.',
108+
'required_without_all' => 'The :attribute field is required when none of :values are present.',
109+
'prohibited' => 'The :attribute field is prohibited.',
110+
'prohibited_if' => 'The :attribute field is prohibited when :other is :value.',
111+
'prohibited_unless' => 'The :attribute field is prohibited unless :other is in :values.',
112+
'prohibits' => 'The :attribute field prohibits :other from being present.',
113+
'same' => 'The :attribute and :other must match.',
114+
'size' => [
115+
'numeric' => 'The :attribute must be :size.',
116+
'file' => 'The :attribute must be :size kilobytes.',
117+
'string' => 'The :attribute must be :size characters.',
118+
'array' => 'The :attribute must contain :size items.',
119+
],
120+
'starts_with' => 'The :attribute must start with one of the following: :values.',
121+
'string' => 'The :attribute must be a string.',
122+
'timezone' => 'The :attribute must be a valid timezone.',
123+
'unique' => 'The :attribute has already been taken.',
124+
'uploaded' => 'The :attribute failed to upload.',
125+
'url' => 'The :attribute must be a valid URL.',
126+
'uuid' => 'The :attribute must be a valid UUID.',
127+
128+
/*
129+
|--------------------------------------------------------------------------
130+
| Custom Validation Language Lines
131+
|--------------------------------------------------------------------------
132+
|
133+
| Here you may specify custom validation messages for attributes using the
134+
| convention "attribute.rule" to name the lines. This makes it quick to
135+
| specify a specific custom language line for a given attribute rule.
136+
|
137+
*/
138+
139+
'custom' => [
140+
'attribute-name' => [
141+
'rule-name' => 'custom-message',
142+
],
143+
],
144+
145+
/*
146+
|--------------------------------------------------------------------------
147+
| Custom Validation Attributes
148+
|--------------------------------------------------------------------------
149+
|
150+
| The following language lines are used to swap our attribute placeholder
151+
| with something more reader friendly such as "E-Mail Address" instead
152+
| of "email". This simply helps us make our message more expressive.
153+
|
154+
*/
155+
156+
'attributes' => [],
157+
158+
];

src/Core.php

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Coding;
44

5+
use Coding\Exceptions\ApiError;
56
use GuzzleHttp\Client;
67

78
class Core

src/ApiError.php src/Exceptions/ApiError.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Coding;
3+
namespace Coding\Exceptions;
44

55
use Exception;
66

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Coding\Exceptions;
4+
5+
use Exception;
6+
7+
class ValidationException extends Exception
8+
{
9+
}

src/Handlers/Validator.php

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace Coding\Handlers;
4+
5+
use Illuminate\Filesystem\Filesystem;
6+
use Illuminate\Translation\FileLoader;
7+
use Illuminate\Translation\Translator;
8+
use Illuminate\Validation\Factory;
9+
10+
class Validator extends Factory
11+
{
12+
public static function getInstance(): Factory
13+
{
14+
static $validator = null;
15+
if ($validator === null) {
16+
$fileLoader = new FileLoader(new Filesystem(), __DIR__ . '/../../lang');
17+
$translator = new Translator($fileLoader, 'en');
18+
$validator = new Factory($translator);
19+
}
20+
return $validator;
21+
}
22+
}

src/Iteration.php

+15
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
namespace Coding;
44

5+
use Coding\Exceptions\ValidationException;
6+
use Coding\Handlers\Validator;
7+
58
class Iteration
69
{
710
private Core $core;
@@ -13,6 +16,18 @@ public function __construct(string $token, Core $core = null)
1316

1417
public function create(array $data)
1518
{
19+
$validator = Validator::getInstance()->make($data, [
20+
'ProjectName' => 'string|required',
21+
'Name' => 'string|required',
22+
'Goal' => 'string',
23+
'Assignee' => 'integer',
24+
'StartAt' => 'date_format:Y-m-d',
25+
'EndAt' => 'date_format:Y-m-d|after:StartAt',
26+
]);
27+
if ($validator->fails()) {
28+
// TODO Laravel ValidationException no message
29+
throw new ValidationException($validator->errors()->all()[0]);
30+
}
1631
$response = $this->core->request('CreateIteration', $data);
1732
return $response['Iteration'];
1833
}

tests/CoreTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace Coding\Tests;
44

5-
use Coding\ApiError;
5+
use Coding\Exceptions\ApiError;
66
use Coding\Core;
77
use GuzzleHttp\Psr7\Response;
88

tests/IterationTest.php

+56-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
namespace Coding\Tests;
44

55
use Coding\Core;
6+
use Coding\Exceptions\ValidationException;
67
use Coding\Iteration;
78

89
class IterationTest extends TestCase
910
{
10-
public function testCreateSuccess()
11+
public function testCreateSuccessWithOnlyRequiredParams()
1112
{
1213
$coreMock = \Mockery::mock(Core::class, [])->makePartial();
1314

@@ -28,4 +29,58 @@ public function testCreateSuccess()
2829
$result = $iteration->create($data);
2930
$this->assertEquals($response['Iteration'], $result);
3031
}
32+
33+
public function testCreateSuccessWithAllParams()
34+
{
35+
$coreMock = \Mockery::mock(Core::class, [])->makePartial();
36+
37+
$response = json_decode(
38+
file_get_contents($this->dataPath('CreateIterationResponse.json')),
39+
true
40+
)['Response'];
41+
$startAt = $this->faker->date();
42+
$data = [
43+
'ProjectName' => $this->projectName,
44+
'Name' => $this->faker->title,
45+
'Goal' => $this->faker->sentence,
46+
'Assignee' => $this->faker->randomNumber(),
47+
'StartAt' => $startAt,
48+
'EndAt' => date('Y-m-d', strtotime($startAt) + $this->faker->randomDigitNotZero() * 86400),
49+
];
50+
$coreMock->shouldReceive('request')->times(1)->withArgs([
51+
'CreateIteration',
52+
$data
53+
])->andReturn($response);
54+
55+
$iteration = new Iteration($this->token, $coreMock);
56+
$result = $iteration->create($data);
57+
$this->assertEquals($response['Iteration'], $result);
58+
}
59+
60+
public function testCreateFailedRequired()
61+
{
62+
$data = [
63+
'ProjectName' => $this->projectName,
64+
];
65+
66+
$iteration = new Iteration($this->token);
67+
$this->expectException(ValidationException::class);
68+
$this->expectExceptionMessage('The name field is required.');
69+
$iteration->create($data);
70+
}
71+
72+
public function testCreateFailedBefore()
73+
{
74+
$data = [
75+
'ProjectName' => $this->projectName,
76+
'Name' => $this->faker->title,
77+
'StartAt' => '2021-10-23',
78+
'EndAt' => '2021-10-22',
79+
];
80+
81+
$iteration = new Iteration($this->token);
82+
$this->expectException(ValidationException::class);
83+
$this->expectExceptionMessage('The end at must be a date after start at.');
84+
$iteration->create($data);
85+
}
3186
}

0 commit comments

Comments
 (0)