mirror of
https://github.com/anikeen-com/id.git
synced 2026-11-07 16:18:33 +00:00
Find crop codes under crop and document unreadable avatar files
This commit is contained in:
5 files changed
+52
-5
No files matched your search
@@ -1187,7 +1187,7 @@ $user->removeAvatar(); // back
|
|||||||
| 401 | `unauthenticated` |
|
| 401 | `unauthenticated` |
|
||||||
| 403 | `missing_scope` (no `user` scope), `user_token_required` (an app token: there is no user to change) |
|
| 403 | `missing_scope` (no `user` scope), `user_token_required` (an app token: there is no user to change) |
|
||||||
| 413 | `avatar_too_large` (the upload is larger than the server accepts) |
|
| 413 | `avatar_too_large` (the upload is larger than the server accepts) |
|
||||||
| 422 | `ValidationFailedException`, codes per field: `name_invalid`, `username_taken`, `username_invalid`, `email_taken`, `email_invalid`, `avatar_invalid`, `avatar_too_large`, `crop_invalid`, else `validation_failed` |
|
| 422 | `ValidationFailedException`, codes per field: `name_invalid`, `username_taken`, `username_invalid`, `email_taken`, `email_invalid`, `avatar_invalid`, `avatar_too_large`, `crop_invalid` (keyed by `crop.x`, `crop.y` or `crop.size`; `errorCodeFor('crop')` finds it), else `validation_failed` |
|
||||||
| 429 | `too_many_requests` |
|
| 429 | `too_many_requests` |
|
||||||
|
|
||||||
Branch on the codes, the messages are for people and may change.
|
Branch on the codes, the messages are for people and may change.
|
||||||
|
|||||||
+1
-1
@@ -1187,7 +1187,7 @@ $user->removeAvatar(); // back
|
|||||||
| 401 | `unauthenticated` |
|
| 401 | `unauthenticated` |
|
||||||
| 403 | `missing_scope` (no `user` scope), `user_token_required` (an app token: there is no user to change) |
|
| 403 | `missing_scope` (no `user` scope), `user_token_required` (an app token: there is no user to change) |
|
||||||
| 413 | `avatar_too_large` (the upload is larger than the server accepts) |
|
| 413 | `avatar_too_large` (the upload is larger than the server accepts) |
|
||||||
| 422 | `ValidationFailedException`, codes per field: `name_invalid`, `username_taken`, `username_invalid`, `email_taken`, `email_invalid`, `avatar_invalid`, `avatar_too_large`, `crop_invalid`, else `validation_failed` |
|
| 422 | `ValidationFailedException`, codes per field: `name_invalid`, `username_taken`, `username_invalid`, `email_taken`, `email_invalid`, `avatar_invalid`, `avatar_too_large`, `crop_invalid` (keyed by `crop.x`, `crop.y` or `crop.size`; `errorCodeFor('crop')` finds it), else `validation_failed` |
|
||||||
| 429 | `too_many_requests` |
|
| 429 | `too_many_requests` |
|
||||||
|
|
||||||
Branch on the codes, the messages are for people and may change.
|
Branch on the codes, the messages are for people and may change.
|
||||||
|
|||||||
@@ -72,9 +72,10 @@ trait ManagesProfile
|
|||||||
* @param array{x: int, y: int, size: int}|null $crop the square to use, in pixels of the
|
* @param array{x: int, y: int, size: int}|null $crop the square to use, in pixels of the
|
||||||
* upright picture; the centre without one
|
* upright picture; the centre without one
|
||||||
* @throws ValidationFailedException 422 with the codes "avatar_invalid", "avatar_too_large",
|
* @throws ValidationFailedException 422 with the codes "avatar_invalid", "avatar_too_large",
|
||||||
* "crop_invalid"
|
* "crop_invalid" (keyed by crop.x, crop.y or crop.size; errorCodeFor('crop') finds them)
|
||||||
* @throws ApiException 413 "avatar_too_large", 401, 403 "missing_scope", "user_token_required",
|
* @throws ApiException 413 "avatar_too_large", 401, 403 "missing_scope", "user_token_required",
|
||||||
* 429 "too_many_requests" (also more than 10 uploads an hour)
|
* 429 "too_many_requests" (also more than 10 uploads an hour)
|
||||||
|
* @throws \RuntimeException when the file can't be opened (nothing is sent then)
|
||||||
*/
|
*/
|
||||||
public function uploadAvatar(string|SplFileInfo $file, ?array $crop = null): User
|
public function uploadAvatar(string|SplFileInfo $file, ?array $crop = null): User
|
||||||
{
|
{
|
||||||
@@ -129,4 +130,4 @@ trait ManagesProfile
|
|||||||
|
|
||||||
return new User($result->data);
|
return new User($result->data);
|
||||||
}
|
}
|
||||||
}
|
}␍
|
||||||
@@ -19,9 +19,25 @@ class ValidationFailedException extends ApiException
|
|||||||
return is_object($codes) || is_array($codes) ? array_map('strval', (array) $codes) : [];
|
return is_object($codes) || is_array($codes) ? array_map('strval', (array) $codes) : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The code of $field, or of its first failed sub-field when the codes are keyed by those
|
||||||
|
* (`crop` finds `crop.x`, `crop.y` or `crop.size`).
|
||||||
|
*/
|
||||||
public function errorCodeFor(string $field): ?string
|
public function errorCodeFor(string $field): ?string
|
||||||
{
|
{
|
||||||
return $this->errorCodes()[$field] ?? null;
|
$codes = $this->errorCodes();
|
||||||
|
|
||||||
|
if (isset($codes[$field])) {
|
||||||
|
return $codes[$field];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($codes as $key => $code) {
|
||||||
|
if (str_starts_with((string) $key, $field . '.')) {
|
||||||
|
return $code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -198,6 +198,36 @@ class UserProfileTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_crop_codes_are_found_under_the_crop_field(): void
|
||||||
|
{
|
||||||
|
$path = tempnam(sys_get_temp_dir(), 'avatar');
|
||||||
|
file_put_contents($path, 'a picture');
|
||||||
|
$this->respond(422, $this->validationError(['crop.size' => 'crop_invalid']));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->owner()->uploadAvatar($path, ['x' => 0, 'y' => 0]);
|
||||||
|
$this->fail('No exception thrown');
|
||||||
|
} catch (ValidationFailedException $e) {
|
||||||
|
$this->assertSame('crop_invalid', $e->errorCodeFor('crop'));
|
||||||
|
$this->assertSame('crop_invalid', $e->errorCodeFor('crop.size'));
|
||||||
|
$this->assertNull($e->errorCodeFor('cro'));
|
||||||
|
$this->assertNull($e->errorCodeFor('avatar'));
|
||||||
|
} finally {
|
||||||
|
unlink($path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_an_unreadable_file_throws_before_anything_is_sent(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->owner()->uploadAvatar(sys_get_temp_dir() . '/missing-avatar-' . uniqid() . '.png');
|
||||||
|
$this->fail('No exception thrown');
|
||||||
|
} catch (\RuntimeException $e) {
|
||||||
|
$this->assertNotInstanceOf(ApiException::class, $e);
|
||||||
|
$this->assertCount(0, $this->sent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function test_remove_avatar_deletes_the_upload_and_clears_the_cache(): void
|
public function test_remove_avatar_deletes_the_upload_and_clears_the_cache(): void
|
||||||
{
|
{
|
||||||
$owner = $this->owner();
|
$owner = $this->owner();
|
||||||
|
|||||||
Reference in new issue
Block a user