diff --git a/app/Http/Requests/Api/Post/StorePostRequest.php b/app/Http/Requests/Api/Post/StorePostRequest.php index 10c35c39..00b98f24 100644 --- a/app/Http/Requests/Api/Post/StorePostRequest.php +++ b/app/Http/Requests/Api/Post/StorePostRequest.php @@ -9,6 +9,7 @@ use App\Models\SocialAccount; use App\Rules\ContentFitsPlatformLimits; use App\Rules\ContentTypeMatchesPlatform; +use App\Support\PostMediaRules; use App\Support\PostPlatformMetaRules; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Collection; @@ -35,15 +36,7 @@ public function rules(): array [new ContentFitsPlatformLimits($this->resolveSelectedPlatforms($workspaceId))] ), ], - 'media' => ['sometimes', 'array'], - 'media.*.id' => ['sometimes', 'nullable', 'string'], - 'media.*.path' => ['sometimes', 'nullable', 'string', 'max:500'], - 'media.*.url' => ['required', 'string', 'max:2048', 'url:http,https'], - 'media.*.type' => ['sometimes', 'nullable', 'string', 'max:32'], - 'media.*.mime_type' => ['sometimes', 'nullable', 'string', 'max:255'], - 'media.*.original_filename' => ['sometimes', 'nullable', 'string', 'max:500'], - 'media.*.size' => ['sometimes', 'nullable', 'integer'], - 'media.*.meta' => ['sometimes', 'nullable', 'array'], + ...PostMediaRules::rules(hosted: false), 'platforms' => ['required', 'array', 'min:1'], 'platforms.*.social_account_id' => [ 'required', diff --git a/app/Http/Requests/Api/Post/UpdatePostRequest.php b/app/Http/Requests/Api/Post/UpdatePostRequest.php index a6bf68ea..ccd89e56 100644 --- a/app/Http/Requests/Api/Post/UpdatePostRequest.php +++ b/app/Http/Requests/Api/Post/UpdatePostRequest.php @@ -12,6 +12,7 @@ use App\Rules\ContentFitsPlatformLimits; use App\Rules\ContentTypeCompatibleWithMedia; use App\Rules\ContentTypeMatchesPostPlatform; +use App\Support\PostMediaRules; use App\Support\PostPlatformMetaRules; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Collection; @@ -44,15 +45,7 @@ public function rules(): array [new ContentFitsPlatformLimits($this->resolveSelectedPlatforms())] ), ], - 'media' => ['sometimes', 'array'], - 'media.*.id' => ['sometimes', 'nullable', 'string'], - 'media.*.path' => ['sometimes', 'nullable', 'string', 'max:500'], - 'media.*.url' => ['required', 'string', 'max:2048', 'url:http,https'], - 'media.*.type' => ['sometimes', 'nullable', 'string', 'max:32'], - 'media.*.mime_type' => ['sometimes', 'nullable', 'string', 'max:255'], - 'media.*.original_filename' => ['sometimes', 'nullable', 'string', 'max:500'], - 'media.*.size' => ['sometimes', 'nullable', 'integer'], - 'media.*.meta' => ['sometimes', 'nullable', 'array'], + ...PostMediaRules::rules(hosted: false), 'platforms' => ['sometimes', 'array'], 'platforms.*.id' => ['required', 'uuid', Rule::exists('post_platforms', 'id')->where('post_id', $this->route('post') instanceof Post ? $this->route('post')->id : $this->route('post'))], 'platforms.*.content_type' => [ diff --git a/app/Http/Requests/App/Post/UpdatePostRequest.php b/app/Http/Requests/App/Post/UpdatePostRequest.php index c246ff57..18d7f539 100644 --- a/app/Http/Requests/App/Post/UpdatePostRequest.php +++ b/app/Http/Requests/App/Post/UpdatePostRequest.php @@ -4,12 +4,12 @@ namespace App\Http\Requests\App\Post; -use App\Enums\Media\Source; use App\Enums\Post\Status; use App\Enums\PostPlatform\ContentType; use App\Enums\SocialAccount\Platform; use App\Rules\ContentFitsPlatformLimits; use App\Rules\ContentTypeCompatibleWithMedia; +use App\Support\PostMediaRules; use App\Support\PostPlatformMetaRules; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Collection; @@ -42,17 +42,7 @@ public function rules(): array [new ContentFitsPlatformLimits($this->resolveSelectedPlatforms())] ), ], - 'media' => ['sometimes', 'array'], - 'media.*.id' => ['required', 'string'], - 'media.*.path' => ['required', 'string', 'max:500'], - 'media.*.url' => ['required', 'string', 'max:2048'], - 'media.*.type' => ['sometimes', 'nullable', 'string', 'max:32'], - 'media.*.mime_type' => ['sometimes', 'nullable', 'string', 'max:255'], - 'media.*.original_filename' => ['sometimes', 'nullable', 'string', 'max:500'], - 'media.*.size' => ['sometimes', 'nullable', 'integer'], - 'media.*.meta' => ['sometimes', 'nullable', 'array'], - 'media.*.source' => ['sometimes', 'nullable', 'string', Rule::in(array_column(Source::cases(), 'value'))], - 'media.*.source_meta' => ['sometimes', 'nullable', 'array'], + ...PostMediaRules::rules(hosted: true), 'scheduled_at' => [ 'sometimes', 'nullable', diff --git a/app/Support/PostMediaRules.php b/app/Support/PostMediaRules.php new file mode 100644 index 00000000..ca9ca763 --- /dev/null +++ b/app/Support/PostMediaRules.php @@ -0,0 +1,47 @@ + + */ + public static function rules(bool $hosted): array + { + $rules = [ + 'media' => ['sometimes', 'array'], + 'media.*.id' => $hosted ? ['required', 'string'] : ['sometimes', 'nullable', 'string'], + 'media.*.path' => $hosted ? ['required', 'string', 'max:500'] : ['sometimes', 'nullable', 'string', 'max:500'], + 'media.*.url' => $hosted + ? ['required', 'string', 'max:2048'] + : ['required', 'string', 'max:2048', 'url:http,https'], + 'media.*.type' => ['sometimes', 'nullable', 'string', 'max:32'], + 'media.*.mime_type' => ['sometimes', 'nullable', 'string', 'max:255'], + 'media.*.original_filename' => ['sometimes', 'nullable', 'string', 'max:500'], + 'media.*.size' => ['sometimes', 'nullable', 'integer'], + 'media.*.meta' => ['sometimes', 'nullable', 'array'], + ]; + + if ($hosted) { + $rules['media.*.source'] = ['sometimes', 'nullable', 'string', Rule::in(array_column(Source::cases(), 'value'))]; + $rules['media.*.source_meta'] = ['sometimes', 'nullable', 'array']; + } + + return $rules; + } +} diff --git a/tests/Unit/Support/PostMediaRulesTest.php b/tests/Unit/Support/PostMediaRulesTest.php new file mode 100644 index 00000000..beb02baa --- /dev/null +++ b/tests/Unit/Support/PostMediaRulesTest.php @@ -0,0 +1,38 @@ +toContain('required') + ->and($rules['media.*.path'])->toContain('required') + ->and($rules)->toHaveKey('media.*.source') + ->and($rules)->toHaveKey('media.*.source_meta'); +}); + +test('api media rules accept a bare external url and omit source', function () { + $rules = PostMediaRules::rules(hosted: false); + + expect($rules['media.*.id'])->toContain('nullable') + ->and($rules['media.*.url'])->toContain('url:http,https') + ->and($rules)->not->toHaveKey('media.*.source') + ->and($rules)->not->toHaveKey('media.*.source_meta'); +}); + +test('both variants keep the shared item keys so validated() preserves them', function () { + foreach ([true, false] as $hosted) { + expect(PostMediaRules::rules(hosted: $hosted))->toHaveKeys([ + 'media.*.id', + 'media.*.path', + 'media.*.url', + 'media.*.type', + 'media.*.mime_type', + 'media.*.original_filename', + 'media.*.size', + 'media.*.meta', + ]); + } +});