diff --git a/app/Models/Project.php b/app/Models/Project.php new file mode 100644 index 0000000..71a9e44 --- /dev/null +++ b/app/Models/Project.php @@ -0,0 +1,28 @@ +app->bind(AccountCommandInterface::class, AccountCommand::class); $this->app->bind(AccountMapperInterface::class, AccountMapper::class); + + $this->app->bind(ProjectCommandInterface::class, ProjectCommand::class); + $this->app->bind(ProjectMapperInterface::class, ProjectMapper::class); } /** diff --git a/core/Application/Project/Commons/Exceptions/ProjectAlreadyExistsException.php b/core/Application/Project/Commons/Exceptions/ProjectAlreadyExistsException.php new file mode 100644 index 0000000..615b7a1 --- /dev/null +++ b/core/Application/Project/Commons/Exceptions/ProjectAlreadyExistsException.php @@ -0,0 +1,10 @@ +projectMapper->existsByName( + $createProjectInput->name, + $authUser->getAccount() + ); + if ($hasProjectWithSameName) { + throw new ProjectAlreadyExistsException( + 'Project already exists with name ([)' . $createProjectInput->name . ')', + ResponseStatus::UNPROCESSABLE_ENTITY->value + ); + } + $projectEntity = ProjectEntity::forCreate( + name: $createProjectInput->name, + description: $createProjectInput->description, + user: $authUser, + account: $authUser->getAccount(), + uuid: $this->framework->uuid()->uuid7Generate(), + startAt: $createProjectInput->startAt, + finishAt: $createProjectInput->finishAt + + ); + + return $this->projectCommand->create($projectEntity); + } +} diff --git a/core/Application/Project/Create/inputs/CreateProjectInput.php b/core/Application/Project/Create/inputs/CreateProjectInput.php new file mode 100644 index 0000000..0d366d2 --- /dev/null +++ b/core/Application/Project/Create/inputs/CreateProjectInput.php @@ -0,0 +1,16 @@ + + */ +class ProjectFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => $this->faker->name, + 'description' => $this->faker->text, + 'start_at' => $this->faker->dateTime, + 'finish_at' => $this, + 'uuid' => $this->faker->uuid, + 'created_by_user_id' => User::factory(), + 'account_id' => Account::factory(), + ]; + } +} diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php index 8196145..7838208 100644 --- a/database/migrations/0001_01_01_000000_create_users_table.php +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -12,12 +12,8 @@ public function up(): void { Schema::create('users', function (Blueprint $table) { $table->id(); - $table->foreignId('account_id') - ->nullable() - ->default(null) - ->constrained() - ->onDelete('cascade'); - $table->integer('role')->default(0); + $table->foreignId('account_id')->nullable()->default(null); + $table->smallInteger('role')->default(0); $table->string('name'); $table->string('email')->unique(); $table->date('birthday')->nullable(); diff --git a/database/migrations/2024_08_25_214144_create_projects_table.php b/database/migrations/2024_08_25_214144_create_projects_table.php new file mode 100644 index 0000000..c004ec2 --- /dev/null +++ b/database/migrations/2024_08_25_214144_create_projects_table.php @@ -0,0 +1,35 @@ +id(); + $table->string('name', 255); + $table->string('description', 500); + $table->foreignId('created_by_user_id')->constrained('users'); + $table->foreignId('account_id')->constrained('accounts'); + $table->dateTime('start_at')->default(null)->nullable(); + $table->dateTime('finish_at')->default(null)->nullable(); + $table->smallInteger('status')->default(null)->nullable(); + $table->timestamps(); + $table->softDeletes(); + $table->uuid()->unique(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('projects'); + } +}; diff --git a/infra/Database/Project/Command/ProjectCommand.php b/infra/Database/Project/Command/ProjectCommand.php new file mode 100644 index 0000000..bdf4972 --- /dev/null +++ b/infra/Database/Project/Command/ProjectCommand.php @@ -0,0 +1,29 @@ +name = $entity->getName(); + $project->description = $entity->getDescription(); + $project->account_id = $entity->getAccount()->getId(); + $project->created_by_user_id = $entity->getUser()->getId(); + $project->status = null; + $project->start_at = $entity->getStartAt(); + $project->finish_at = $entity->getFinishAt(); + $project->uuid = $entity->getUuid()->toString(); + + $project->save(); + + $entity->setId($project->id); + return $entity; + } +} diff --git a/infra/Database/Project/Mapper/ProjectMapper.php b/infra/Database/Project/Mapper/ProjectMapper.php new file mode 100644 index 0000000..3ec6235 --- /dev/null +++ b/infra/Database/Project/Mapper/ProjectMapper.php @@ -0,0 +1,36 @@ +existsByName($name, $accountEntity); + } + + public function existsByName(string $name, AccountEntity $accountEntity): bool + { + return Project::query() + ->where('name', 'like', $name) + ->where('account_id', '=', $accountEntity->getId()) + ->exists(); + } +} diff --git a/tests/ExampleTest.php b/tests/ExampleTest.php index 9f5940b..748d478 100644 --- a/tests/ExampleTest.php +++ b/tests/ExampleTest.php @@ -2,26 +2,9 @@ namespace Tests; -use Illuminate\Foundation\Testing\DatabaseMigrations; - /** - * @group relations + * @group test_ */ class ExampleTest extends TestCase { - use DatabaseMigrations; - - /** - * A basic feature test example. - */ - /*public function test_example(): void - { - $user = User::factory()->create(); - AccountUser::factory()->create([ - 'user_id' => $user->id, - ]); - $user->loadMissing('accounts'); - dd($user->accounts->first()->usersWithPivotData->first()->toArray()); - dd($user); - }*/ } diff --git a/tests/Integration/UseCases/Project/CreateProjectUseCaseTest.php b/tests/Integration/UseCases/Project/CreateProjectUseCaseTest.php new file mode 100644 index 0000000..5879042 --- /dev/null +++ b/tests/Integration/UseCases/Project/CreateProjectUseCaseTest.php @@ -0,0 +1,96 @@ +useCase->execute( + createProjectInput: $input, + authUser: UserEntity::forIdentify( + id: $this->user->id, + uuid: Uuid::fromString($this->user->uuid), + role: UserRoles::ADMIN, + accountId: $this->user->account_id + ) + ); + + $this->assertDatabaseHas('projects', [ + 'id' => $projectEntity->getId(), + 'uuid' => $projectEntity->getUuid()->toString(), + 'name' => 'Nome', + 'description' => 'Desc', + 'start_at' => null, + 'finish_at' => null + ]); + } + + + public function test_create_project_success_with_dates() + { + $input = new CreateProjectInput( + name: 'Nome', + description: 'Desc', + startAt: now(), + finishAt: now()->addYears() + ); + $projectEntity = $this->useCase->execute( + createProjectInput: $input, + authUser: UserEntity::forIdentify( + id: $this->user->id, + uuid: Uuid::fromString($this->user->uuid), + role: UserRoles::ADMIN, + accountId: $this->user->account_id + ) + ); + + $this->assertDatabaseHas('projects', [ + 'id' => $projectEntity->getId(), + 'uuid' => $projectEntity->getUuid()->toString(), + 'name' => 'Nome', + 'description' => 'Desc', + 'start_at' => $input->startAt, + 'finish_at' => $input->finishAt + ]); + } + + protected function setUp(): void + { + parent::setUp(); + $this->useCase = new CreateProjectUseCase( + $this->app->make(FrameworkContract::class), + $this->app->make(ProjectCommandInterface::class), + $this->app->make(ProjectMapperInterface::class) + ); + $this->user = User::factory()->create([ + 'role' => UserRoles::ADMIN + ]); + } +} diff --git a/tests/Unit/Project/ProjectEntityTest.php b/tests/Unit/Project/ProjectEntityTest.php index d985414..44f2bca 100644 --- a/tests/Unit/Project/ProjectEntityTest.php +++ b/tests/Unit/Project/ProjectEntityTest.php @@ -20,7 +20,7 @@ class ProjectEntityTest extends TestCase { public function test_create_project_success_without_dates() { - ProjectEntity::create( + ProjectEntity::forCreate( name: 'Project Name', description: 'Project Description', user: UserEntity::forIdentify( @@ -41,7 +41,7 @@ public function test_create_project_success_without_dates() public function test_create_project_success_with_dates() { $this->expectNotToPerformAssertions(); - ProjectEntity::create( + ProjectEntity::forCreate( name: 'Project Name', description: 'Project Description', user: UserEntity::forIdentify( @@ -62,7 +62,7 @@ public function test_create_project_success_with_dates() public function test_create_project_success_with_date_start_at_null_and_finished_at_now() { $this->expectException(DateRequiredException::class); - ProjectEntity::create( + ProjectEntity::forCreate( name: 'Project Name', description: 'Project Description', user: UserEntity::forIdentify( @@ -82,7 +82,7 @@ public function test_create_project_success_with_date_start_at_null_and_finished public function test_create_project_fail_with_date_start_at_after_finished_at() { $this->expectException(DateMustBeBeforeOtherException::class); - ProjectEntity::create( + ProjectEntity::forCreate( name: 'Project Name', description: 'Project Description', user: UserEntity::forIdentify( @@ -103,7 +103,7 @@ public function test_create_project_fail_with_date_start_at_after_finished_at() public function test_create_project_fail_with_date_start_at_equal_finished_at() { $this->expectException(DatesMustBeDifferntsException::class); - ProjectEntity::create( + ProjectEntity::forCreate( 'Project Name', 'Project Description', UserEntity::forIdentify( @@ -124,7 +124,7 @@ public function test_create_project_fail_with_date_start_at_equal_finished_at() public function test_create_project_fail_with_date_start_at_before_now() { $this->expectException(DateMustBeInCurrentDayException::class); - ProjectEntity::create( + ProjectEntity::forCreate( 'Project Name', 'Project Description', UserEntity::forIdentify(