From a1e2f4639bb11bccc1011ce5d4aab983334961f0 Mon Sep 17 00:00:00 2001 From: myrmidex Date: Sun, 6 Jul 2025 01:35:59 +0200 Subject: [PATCH] Add factories --- app/Models/ArticlePublication.php | 4 +- app/Models/Feed.php | 4 + app/Models/Language.php | 5 ++ app/Models/PlatformAccount.php | 5 ++ app/Models/PlatformChannel.php | 5 ++ app/Models/PlatformInstance.php | 5 ++ .../Publishing/ArticlePublishingService.php | 2 +- database/factories/FeedFactory.php | 57 +++++++++++++++ database/factories/LanguageFactory.php | 40 ++++++++++ database/factories/PlatformAccountFactory.php | 52 +++++++++++++ database/factories/PlatformChannelFactory.php | 47 ++++++++++++ .../factories/PlatformInstanceFactory.php | 40 ++++++++++ ...1847_create_article_publications_table.php | 4 +- ...reate_language_platform_instance_table.php | 2 +- docker-compose.yml | 73 +++++++++++++++++++ 15 files changed, 339 insertions(+), 6 deletions(-) create mode 100644 database/factories/FeedFactory.php create mode 100644 database/factories/LanguageFactory.php create mode 100644 database/factories/PlatformAccountFactory.php create mode 100644 database/factories/PlatformChannelFactory.php create mode 100644 database/factories/PlatformInstanceFactory.php create mode 100644 docker-compose.yml diff --git a/app/Models/ArticlePublication.php b/app/Models/ArticlePublication.php index 52b87cc..c0e98ea 100644 --- a/app/Models/ArticlePublication.php +++ b/app/Models/ArticlePublication.php @@ -7,7 +7,7 @@ /** * @property integer $article_id - * @property integer $community_id + * @property integer $platform_channel_id * @property integer $post_id * * @method static create(array $array) @@ -16,7 +16,7 @@ class ArticlePublication extends Model { protected $fillable = [ 'article_id', - 'community_id', + 'platform_channel_id', 'post_id', 'published_at', 'published_by', diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 2b071d6..f274453 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -2,6 +2,8 @@ namespace App\Models; +use Database\Factories\FeedFactory; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; @@ -28,6 +30,8 @@ */ class Feed extends Model { + /** @use HasFactory */ + use HasFactory; private const RECENT_FETCH_THRESHOLD_HOURS = 2; private const DAILY_FETCH_THRESHOLD_HOURS = 24; diff --git a/app/Models/Language.php b/app/Models/Language.php index ccbc850..e7168c9 100644 --- a/app/Models/Language.php +++ b/app/Models/Language.php @@ -2,12 +2,17 @@ namespace App\Models; +use Database\Factories\LanguageFactory; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; class Language extends Model { + /** @use HasFactory */ + use HasFactory; + protected $fillable = [ 'short_code', 'name', diff --git a/app/Models/PlatformAccount.php b/app/Models/PlatformAccount.php index 9debe43..2a65403 100644 --- a/app/Models/PlatformAccount.php +++ b/app/Models/PlatformAccount.php @@ -2,6 +2,8 @@ namespace App\Models; +use Database\Factories\PlatformAccountFactory; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; @@ -29,6 +31,9 @@ */ class PlatformAccount extends Model { + /** @use HasFactory */ + use HasFactory; + protected $fillable = [ 'platform', 'instance_url', diff --git a/app/Models/PlatformChannel.php b/app/Models/PlatformChannel.php index 4e1bd58..b9f1a2c 100644 --- a/app/Models/PlatformChannel.php +++ b/app/Models/PlatformChannel.php @@ -2,6 +2,8 @@ namespace App\Models; +use Database\Factories\PlatformChannelFactory; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; @@ -20,6 +22,9 @@ */ class PlatformChannel extends Model { + /** @use HasFactory */ + use HasFactory; + protected $table = 'platform_channels'; protected $fillable = [ diff --git a/app/Models/PlatformInstance.php b/app/Models/PlatformInstance.php index 9d2c5a4..e6a1a6f 100644 --- a/app/Models/PlatformInstance.php +++ b/app/Models/PlatformInstance.php @@ -3,6 +3,8 @@ namespace App\Models; use App\Enums\PlatformEnum; +use Database\Factories\PlatformInstanceFactory; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -18,6 +20,9 @@ */ class PlatformInstance extends Model { + /** @use HasFactory */ + use HasFactory; + protected $fillable = [ 'platform', 'url', diff --git a/app/Services/Publishing/ArticlePublishingService.php b/app/Services/Publishing/ArticlePublishingService.php index 5d369aa..79e5cc2 100644 --- a/app/Services/Publishing/ArticlePublishingService.php +++ b/app/Services/Publishing/ArticlePublishingService.php @@ -51,7 +51,7 @@ private function publishToChannel(Article $article, array $extractedData, $chann $publication = ArticlePublication::create([ 'article_id' => $article->id, 'post_id' => $postData['post_view']['post']['id'], - 'community_id' => $channel->channel_id, + 'platform_channel_id' => $channel->id, 'published_by' => $account->username, 'published_at' => now(), 'platform' => $channel->platformInstance->platform->value, diff --git a/database/factories/FeedFactory.php b/database/factories/FeedFactory.php new file mode 100644 index 0000000..51b212a --- /dev/null +++ b/database/factories/FeedFactory.php @@ -0,0 +1,57 @@ + + */ +class FeedFactory extends Factory +{ + protected $model = Feed::class; + + public function definition(): array + { + return [ + 'name' => $this->faker->words(3, true), + 'url' => $this->faker->url(), + 'type' => $this->faker->randomElement(['website', 'rss']), + 'language_id' => Language::factory(), + 'description' => $this->faker->optional()->sentence(), + 'settings' => [], + 'is_active' => true, + 'last_fetched_at' => null, + ]; + } + + public function inactive(): static + { + return $this->state(fn (array $attributes) => [ + 'is_active' => false, + ]); + } + + public function website(): static + { + return $this->state(fn (array $attributes) => [ + 'type' => 'website', + ]); + } + + public function rss(): static + { + return $this->state(fn (array $attributes) => [ + 'type' => 'rss', + ]); + } + + public function recentlyFetched(): static + { + return $this->state(fn (array $attributes) => [ + 'last_fetched_at' => now()->subHour(), + ]); + } +} \ No newline at end of file diff --git a/database/factories/LanguageFactory.php b/database/factories/LanguageFactory.php new file mode 100644 index 0000000..3e03213 --- /dev/null +++ b/database/factories/LanguageFactory.php @@ -0,0 +1,40 @@ + + */ +class LanguageFactory extends Factory +{ + protected $model = Language::class; + + public function definition(): array + { + return [ + 'short_code' => $this->faker->unique()->languageCode(), + 'name' => $this->faker->unique()->word(), + 'native_name' => $this->faker->optional()->word(), + 'is_active' => true, + ]; + } + + public function inactive(): static + { + return $this->state(fn (array $attributes) => [ + 'is_active' => false, + ]); + } + + public function english(): static + { + return $this->state(fn (array $attributes) => [ + 'short_code' => 'en', + 'name' => 'English', + 'native_name' => 'English', + ]); + } +} \ No newline at end of file diff --git a/database/factories/PlatformAccountFactory.php b/database/factories/PlatformAccountFactory.php new file mode 100644 index 0000000..6c01c17 --- /dev/null +++ b/database/factories/PlatformAccountFactory.php @@ -0,0 +1,52 @@ + + */ +class PlatformAccountFactory extends Factory +{ + protected $model = PlatformAccount::class; + + public function definition(): array + { + return [ + 'platform' => PlatformEnum::LEMMY, + 'instance_url' => 'https://lemmy.' . $this->faker->domainName(), + 'username' => $this->faker->userName(), + 'password' => 'test-password', + 'settings' => [], + 'is_active' => true, + 'last_tested_at' => null, + 'status' => 'untested', + ]; + } + + public function inactive(): static + { + return $this->state(fn (array $attributes) => [ + 'is_active' => false, + ]); + } + + public function tested(): static + { + return $this->state(fn (array $attributes) => [ + 'last_tested_at' => now()->subHours(2), + 'status' => 'working', + ]); + } + + public function failed(): static + { + return $this->state(fn (array $attributes) => [ + 'last_tested_at' => now()->subHours(2), + 'status' => 'failed', + ]); + } +} \ No newline at end of file diff --git a/database/factories/PlatformChannelFactory.php b/database/factories/PlatformChannelFactory.php new file mode 100644 index 0000000..3643da2 --- /dev/null +++ b/database/factories/PlatformChannelFactory.php @@ -0,0 +1,47 @@ + + */ +class PlatformChannelFactory extends Factory +{ + protected $model = PlatformChannel::class; + + public function definition(): array + { + return [ + 'platform_instance_id' => PlatformInstance::factory(), + 'channel_id' => $this->faker->slug(2), + 'name' => $this->faker->words(2, true), + 'display_name' => $this->faker->words(2, true), + 'language_id' => Language::factory(), + 'description' => $this->faker->optional()->sentence(), + 'is_active' => true, + ]; + } + + public function inactive(): static + { + return $this->state(fn (array $attributes) => [ + 'is_active' => false, + ]); + } + + public function community(string $name = null): static + { + $communityName = $name ?: $this->faker->word(); + + return $this->state(fn (array $attributes) => [ + 'channel_id' => strtolower($communityName), + 'name' => $communityName, + 'display_name' => ucfirst($communityName), + ]); + } +} \ No newline at end of file diff --git a/database/factories/PlatformInstanceFactory.php b/database/factories/PlatformInstanceFactory.php new file mode 100644 index 0000000..182f9dc --- /dev/null +++ b/database/factories/PlatformInstanceFactory.php @@ -0,0 +1,40 @@ + + */ +class PlatformInstanceFactory extends Factory +{ + protected $model = PlatformInstance::class; + + public function definition(): array + { + return [ + 'platform' => 'lemmy', + 'name' => $this->faker->words(2, true), + 'url' => $this->faker->url(), + 'is_active' => true, + ]; + } + + public function inactive(): static + { + return $this->state(fn (array $attributes) => [ + 'is_active' => false, + ]); + } + + public function lemmy(): static + { + return $this->state(fn (array $attributes) => [ + 'platform' => 'lemmy', + 'name' => 'Lemmy ' . $this->faker->word(), + 'url' => 'https://lemmy.' . $this->faker->domainName(), + ]); + } +} \ No newline at end of file diff --git a/database/migrations/2025_06_29_181847_create_article_publications_table.php b/database/migrations/2025_06_29_181847_create_article_publications_table.php index 04eb974..d0b77cd 100644 --- a/database/migrations/2025_06_29_181847_create_article_publications_table.php +++ b/database/migrations/2025_06_29_181847_create_article_publications_table.php @@ -12,14 +12,14 @@ public function up(): void $table->id(); $table->foreignId('article_id')->constrained()->onDelete('cascade'); $table->string('post_id'); - $table->unsignedBigInteger('community_id'); + $table->unsignedBigInteger('platform_channel_id'); $table->string('platform')->default('lemmy'); $table->json('publication_data')->nullable(); $table->timestamp('published_at'); $table->string('published_by'); $table->timestamps(); - $table->unique(['article_id', 'platform', 'community_id']); + $table->unique(['article_id', 'platform', 'platform_channel_id'], 'article_pub_unique'); }); } diff --git a/database/migrations/2025_07_05_142443_create_language_platform_instance_table.php b/database/migrations/2025_07_05_142443_create_language_platform_instance_table.php index e876d80..bbaac64 100644 --- a/database/migrations/2025_07_05_142443_create_language_platform_instance_table.php +++ b/database/migrations/2025_07_05_142443_create_language_platform_instance_table.php @@ -16,7 +16,7 @@ public function up(): void $table->boolean('is_default')->default(false); // Whether this is the default language for this instance $table->timestamps(); - $table->unique(['language_id', 'platform_instance_id']); + $table->unique(['language_id', 'platform_instance_id'], 'lang_platform_instance_unique'); }); } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..34b7daf --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,73 @@ +services: + laravel.test: + build: + context: ./vendor/laravel/sail/runtimes/8.4 + dockerfile: Dockerfile + args: + WWWGROUP: '${WWWGROUP}' + image: sail-8.4/app + extra_hosts: + - 'host.docker.internal:host-gateway' + ports: + - '${APP_PORT:-80}:80' + - '${VITE_PORT:-5173}:${VITE_PORT:-5173}' + environment: + WWWUSER: '${WWWUSER}' + LARAVEL_SAIL: 1 + XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}' + XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}' + IGNITION_LOCAL_SITES_PATH: '${PWD}' + volumes: + - '.:/var/www/html' + networks: + - sail + depends_on: + - mysql + - redis + mysql: + image: 'mysql/mysql-server:8.0' + ports: + - '${FORWARD_DB_PORT:-3306}:3306' + environment: + MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}' + MYSQL_ROOT_HOST: '%' + MYSQL_DATABASE: '${DB_DATABASE}' + MYSQL_USER: '${DB_USERNAME}' + MYSQL_PASSWORD: '${DB_PASSWORD}' + MYSQL_ALLOW_EMPTY_PASSWORD: 1 + volumes: + - 'sail-mysql:/var/lib/mysql' + - './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh' + networks: + - sail + healthcheck: + test: + - CMD + - mysqladmin + - ping + - '-p${DB_PASSWORD}' + retries: 3 + timeout: 5s + redis: + image: 'redis:alpine' + ports: + - '${FORWARD_REDIS_PORT:-6379}:6379' + volumes: + - 'sail-redis:/data' + networks: + - sail + healthcheck: + test: + - CMD + - redis-cli + - ping + retries: 3 + timeout: 5s +networks: + sail: + driver: bridge +volumes: + sail-mysql: + driver: local + sail-redis: + driver: local