diff --git a/app/Http/Controllers/API/AuthController.php b/app/Http/Controllers/API/AuthController.php new file mode 100644 index 0000000..3a6fc88 --- /dev/null +++ b/app/Http/Controllers/API/AuthController.php @@ -0,0 +1,57 @@ +all(), [ + 'username' => 'required|max:55|unique:users', + 'password' => 'required|min:6' + ]); + + if($validator->fails()){ + return response(['error' => $validator->errors()]); + } + + $user = User::create([ + 'username' => $request->username, + 'password' => Hash::make($request->password), + 'role' => $request->role, + ]); + + $accessToken = $user->createToken('authToken')->accessToken; + + return response([ 'user' => $user, 'access_token' => $accessToken]); + } + + public function login(Request $request) + { + $data = $request->all(); + + $validator = Validator::make($data, [ + 'username' => 'required', + 'password' => 'required|min:6' + ]); + + if($validator->fails()){ + return response(['error' => $validator->errors()]); + } + + if (!auth()->attempt($data)) { + return response(['message' => 'Login credentials are invaild']); + } + + $accessToken = auth()->user()->createToken('authToken')->accessToken; + + return response(['access_token' => $accessToken]); + + } +} diff --git a/app/Http/Controllers/API/ProjectController.php b/app/Http/Controllers/API/ProjectController.php new file mode 100644 index 0000000..557e43f --- /dev/null +++ b/app/Http/Controllers/API/ProjectController.php @@ -0,0 +1,90 @@ +where('name', 'LIKE', "%$q%"):$Projects; + $Projects = $Projects->paginate($pageSize); + return response(['Projects' => $Projects]); + + } + + private function checkUserRole() + { + if(auth('api')->user()->role != 'PRODUCT_OWNER'){ + return false; + } + return true; + } + + public function store(Request $request) + { + if(!$this->checkUserRole()){ + return Response::deny('You must be a PRODUCT_OWNER to continue.'); + } + $data = $request->all(); + + $validator = Validator::make($data, [ + 'name' => 'required|max:255', + ]); + + if($validator->fails()){ + return response(['error' => $validator->errors(), 'Validation Error']); + } + + $Project = Project::create($data); + + return response(['Project' => new ProjectResource($Project), 'message' => 'Project created successfully']); + } + + public function show(Project $Project) + { + + return response(['Project' => new ProjectResource($Project)]); + } + + public function update(Request $request, Project $Project) + { + if(!$this->checkUserRole()){ + return Response::deny('You must be a PRODUCT_OWNER to continue.'); + } + $data = $request->all(); + + $validator = Validator::make($data, [ + 'name' => 'required|max:255', + ]); + + if($validator->fails()){ + return response(['error' => $validator->errors(), 'Validation Error']); + } + + $Project->update($data); + + return response(['Project' => new ProjectResource($Project), 'message' => 'Project updated successfully']); + } + + public function destroy(Project $Project) + { + if(!$this->checkUserRole()){ + return Response::deny('You must be a PRODUCT_OWNER to continue.'); + } + $Project->delete(); + + return response(['message' => 'Project deleted successfully']); + } +} diff --git a/app/Http/Controllers/API/TaskController.php b/app/Http/Controllers/API/TaskController.php new file mode 100644 index 0000000..29903b4 --- /dev/null +++ b/app/Http/Controllers/API/TaskController.php @@ -0,0 +1,124 @@ + TaskResource::collection($Tasks)]); + } + + private function checkUserRole() + { + if(auth('api')->user()->role != 'PRODUCT_OWNER'){ + return false; + } + return true; + } + public function store(Request $request) + { + if(!$this->checkUserRole()){ + return Response::deny('You must be a PRODUCT_OWNER to continue creating tasks.'); + } + $data = $request->all(); + $data['status'] = 'NOT_STARTED'; + + $validator = Validator::make($data, [ + 'title' => 'required|max:255', + 'status' => 'required', + 'project_id' => 'required', + 'user_id' => 'required', + ]); + + if($validator->fails()){ + return response(['error' => $validator->errors(), 'Validation Error']); + } + + if(!in_array($data['status'], $this->tasks_status)){ + return Response::deny("You must provide task status from following ['NOT_STARTED', 'IN_PROGRESS', 'READY_FOR_TEST', 'COMPLETED']"); + } + + $user = User::find($request->user_id); + if(!$user){ + return Response::deny("User not found to assign task!"); + } + $project = Project::find($request->project_id); + + if(!$project){ + return Response::deny("Project not found!"); + } + $Task = Task::create($data); + + + $user->role = 'PRODUCT_OWNER'; + + $user->save(); + + return response(['Task' => new TaskResource($Task), 'message' => 'Task created successfully']); + } + + public function show(Task $Task) + { + return response(['Task' => new TaskResource($Task)]); + } + + public function update(Request $request, Task $Task) + { + + $data = $request->all(); + if(!$this->checkUserRole()){ + if(auth('api')->user()->id != $data['user_id']){ + return Response::deny('You have no permission to edit task'); + } + return Response::deny('You must be a PRODUCT_OWNER to continue creating tasks.'); + } + + $validator = Validator::make($data, [ + 'title' => 'required|max:255', + 'status' => 'required', + 'project_id' => 'required', + 'user_id' => 'required', + ]); + + if($validator->fails()){ + return response(['error' => $validator->errors(), 'Validation Error']); + } + if(!in_array($data['status'], $this->tasks_status)){ + return Response::deny("You must provide task status from following ['NOT_STARTED', 'IN_PROGRESS', 'READY_FOR_TEST', 'COMPLETED']"); + } + + $user = User::find($request->user_id); + if(!$user){ + return Response::deny("User not found to assign task!"); + } + $project = Project::find($request->project_id); + + if(!$project){ + return Response::deny("Project not found!"); + } + + $Task->update($data); + + return response(['Task' => new TaskResource($Task), 'message' => 'Task updated successfully']); + } + + public function destroy(Task $Task) + { + $Task->delete(); + + return response(['message' => 'Task deleted successfully']); + } +} diff --git a/app/Http/Controllers/API/UserController.php b/app/Http/Controllers/API/UserController.php new file mode 100644 index 0000000..203bf66 --- /dev/null +++ b/app/Http/Controllers/API/UserController.php @@ -0,0 +1,92 @@ +username != 'Admin'){ + return Response::deny('You must be an administrator to continue accessing users.'); + } + } + + public function index() + { + $Users = User::all(); + return response(['Users' => UserResource::collection($Users)]); + } + + private function checkUserRole() + { + if(auth('api')->user()->role != 'Admin'){ + return false; + } + return true; + } + public function store(Request $request) + { + if(!$this->checkUserRole()){ + return Response::deny('You must be a Admin to continue using users.'); + } + $data = $request->all(); + + $validator = Validator::make($data, [ + 'username' => 'required', + 'password' => 'required|min:6', + 'role' => 'required', + ]); + + if($validator->fails()){ + return response(['error' => $validator->errors(), 'Validation Error']); + } + + $User = User::create($data); + + return response(['User' => new UserResource($User), 'message' => 'User created successfully']); + } + + public function show(User $User) + { + if(!$this->checkUserRole()){ + return Response::deny('You must be a Admin to continue using users.'); + } + return response(['User' => new UserResource($User)]); + } + + public function update(Request $request, User $User) + { + if(!$this->checkUserRole()){ + return Response::deny('You must be a Admin to continue using users.'); + } + $data = $request->all(); + + $validator = Validator::make($data, [ + 'username' => 'required', + 'password' => 'required|min:6', + ]); + + if($validator->fails()){ + return response(['error' => $validator->errors(), 'Validation Error']); + } + + $User->update($data); + + return response(['User' => new UserResource($User), 'message' => 'User updated successfully']); + } + + public function destroy(User $User) + { + if(!$this->checkUserRole()){ + return Response::deny('You must be a Admin to continue using users.'); + } + $User->delete(); + + return response(['message' => 'User deleted successfully']); + } +} diff --git a/app/Http/Resources/ProjectResource.php b/app/Http/Resources/ProjectResource.php new file mode 100644 index 0000000..ba96ecf --- /dev/null +++ b/app/Http/Resources/ProjectResource.php @@ -0,0 +1,13 @@ +belongsTo(Task::class); + } +} diff --git a/app/Models/Task.php b/app/Models/Task.php new file mode 100644 index 0000000..bb198f6 --- /dev/null +++ b/app/Models/Task.php @@ -0,0 +1,23 @@ +hasOne(User::class); + } + + public function projects() + { + return $this->hasOne(Project::class); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 8996368..91fd372 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,43 +2,16 @@ namespace App\Models; -use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Foundation\Auth\User as Authenticatable; -use Illuminate\Notifications\Notifiable; -use Laravel\Sanctum\HasApiTokens; +use Illuminate\Database\Eloquent\Model; +use Laravel\Passport\HasApiTokens; -class User extends Authenticatable +class User extends Model { - use HasApiTokens, HasFactory, Notifiable; - - /** - * The attributes that are mass assignable. - * - * @var array - */ - protected $fillable = [ - 'name', - 'email', - 'password', - ]; - - /** - * The attributes that should be hidden for serialization. - * - * @var array - */ - protected $hidden = [ - 'password', - 'remember_token', - ]; - - /** - * The attributes that should be cast. - * - * @var array - */ - protected $casts = [ - 'email_verified_at' => 'datetime', - ]; + use HasFactory; + + public function Task() + { + return $this->belongsTo(Task::class); + } } diff --git a/app/Models/User_.php b/app/Models/User_.php new file mode 100644 index 0000000..8996368 --- /dev/null +++ b/app/Models/User_.php @@ -0,0 +1,44 @@ + + */ + protected $fillable = [ + 'name', + 'email', + 'password', + ]; + + /** + * The attributes that should be hidden for serialization. + * + * @var array + */ + protected $hidden = [ + 'password', + 'remember_token', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'email_verified_at' => 'datetime', + ]; +} diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 22b77e6..2108ce1 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -4,6 +4,7 @@ use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Gate; +use Laravel\Passport\Passport; class AuthServiceProvider extends ServiceProvider { @@ -13,7 +14,7 @@ class AuthServiceProvider extends ServiceProvider * @var array */ protected $policies = [ - // 'App\Models\Model' => 'App\Policies\ModelPolicy', + 'App\Models\Model' => 'App\Policies\ModelPolicy', ]; /** @@ -24,7 +25,7 @@ class AuthServiceProvider extends ServiceProvider public function boot() { $this->registerPolicies(); - + Passport::routes(); // } } diff --git a/composer.json b/composer.json index 2b0c115..27aee5d 100644 --- a/composer.json +++ b/composer.json @@ -9,6 +9,7 @@ "fruitcake/laravel-cors": "^2.0", "guzzlehttp/guzzle": "^7.0.1", "laravel/framework": "^8.75", + "laravel/passport": "^10.3", "laravel/sanctum": "^2.11", "laravel/tinker": "^2.5" }, diff --git a/config/app.php b/config/app.php index a8d1a82..28b8bdc 100644 --- a/config/app.php +++ b/config/app.php @@ -161,6 +161,7 @@ Illuminate\Translation\TranslationServiceProvider::class, Illuminate\Validation\ValidationServiceProvider::class, Illuminate\View\ViewServiceProvider::class, + Laravel\Passport\PassportServiceProvider::class, /* * Package Service Providers... diff --git a/config/auth.php b/config/auth.php index d8c6cee..99f61e3 100644 --- a/config/auth.php +++ b/config/auth.php @@ -40,6 +40,11 @@ 'driver' => 'session', 'provider' => 'users', ], + 'api' => [ + 'driver' => 'passport', + 'provider' => 'users', + 'hash' => false, + ], ], /* diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2014_10_12_000000_create_users_table.php index 621a24e..f44941f 100644 --- a/database/migrations/2014_10_12_000000_create_users_table.php +++ b/database/migrations/2014_10_12_000000_create_users_table.php @@ -14,12 +14,9 @@ class CreateUsersTable extends Migration public function up() { Schema::create('users', function (Blueprint $table) { - $table->id(); - $table->string('name'); - $table->string('email')->unique(); - $table->timestamp('email_verified_at')->nullable(); + $table->bigIncrements('id'); + $table->string('username')->unique(); $table->string('password'); - $table->rememberToken(); $table->timestamps(); }); } diff --git a/database/migrations/2022_08_25_125451_create_projects_table.php b/database/migrations/2022_08_25_125451_create_projects_table.php new file mode 100644 index 0000000..b34fa90 --- /dev/null +++ b/database/migrations/2022_08_25_125451_create_projects_table.php @@ -0,0 +1,32 @@ +bigIncrements('id'); + $table->string('name')->unique(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('projects'); + } +} diff --git a/database/migrations/2022_08_25_125505_create_tasks_table.php b/database/migrations/2022_08_25_125505_create_tasks_table.php new file mode 100644 index 0000000..8cb416c --- /dev/null +++ b/database/migrations/2022_08_25_125505_create_tasks_table.php @@ -0,0 +1,47 @@ +bigIncrements('id'); + $table->string('title'); + $table->text('description')->nullable(); + $table->string('status'); + $table->unsignedBigInteger('user_id'); + $table->unsignedBigInteger('project_id'); + $table->timestamps(); + + $table->foreign('user_id') + ->references('id') + ->on('users') + ->onDelete('cascade'); + + $table->foreign('project_id') + ->references('id') + ->on('projects') + ->onDelete('cascade'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('tasks'); + } +} diff --git a/routes/api.php b/routes/api.php index eb6fa48..0268ae5 100644 --- a/routes/api.php +++ b/routes/api.php @@ -2,6 +2,10 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; +use App\Http\Controllers\API\AuthController; +use App\Http\Controllers\API\ProjectController; +use App\Http\Controllers\API\UserController; +use App\Http\Controllers\API\TaskController; /* |-------------------------------------------------------------------------- @@ -14,6 +18,13 @@ | */ -Route::middleware('auth:sanctum')->get('/user', function (Request $request) { +Route::middleware('auth:api')->get('/user', function (Request $request) { return $request->user(); }); + +Route::post('/v1/user/register', [AuthController::class, 'register']); +Route::post('/v1/user/login', [AuthController::class, 'login']); + +Route::apiResource('/v1/project', ProjectController::class)->middleware('auth:api'); +Route::apiResource('/v1/users', UserController::class)->middleware('auth:api'); +Route::apiResource('/v1/tasks', TaskController::class)->middleware('auth:api');