diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index e9ddba2b..62072e55 100755 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -68,11 +68,7 @@ jobs: - name: Run database migrations run: php artisan migrate --force - - name: Clear all cache - run: php artisan optimize - - - name: Clear configuration cache - run: php artisan config:clear + - name: Run Laravel tests run: | diff --git a/.github/workflows/postman-api-tests.yml b/.github/workflows/postman-api-tests.yml index c2affbc2..33ffb9a0 100644 --- a/.github/workflows/postman-api-tests.yml +++ b/.github/workflows/postman-api-tests.yml @@ -1,4 +1,4 @@ -name: Scheduled Postman API Tests +name: Scheduled API Tests on: schedule: @@ -23,17 +23,18 @@ jobs: - name: Run Postman Collection run: newman run postman-api-tests.json -r cli,json --reporter-json-export result.json continue-on-error: true - - - - name: Transfer test results via SCP - uses: appleboy/scp-action@v0.1.7 + - name: Set up PHP + uses: nanasess/setup-php@v4 with: - host: ${{ secrets.SSH_HOST }} - username: ${{ secrets.SSH_USER }} - password: ${{ secrets.SSH_PASSWORD }} - source: "result.json" - target: "/var/www/boilerplate-be" - - - name: Clean up - run: rm -f result.json + php-version: '8.2' + + - name: Send parsed result to Hosted Application + run: | + cp .env.example .env + composer install --no-interaction --prefer-dist --optimize-autoloader + echo "APP_URL=${{ secrets.APP_URL }}" >> .env + php artisan store:api-status + + + diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 10970592..93aa0604 100755 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,9 +1,7 @@ name: Code Quality on: - push: - branches: - - dev + pull_request_target: jobs: lint: @@ -31,6 +29,8 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} - name: Set up PHP uses: nanasess/setup-php@v4 diff --git a/.gitignore b/.gitignore index 473babcb..7c36a470 100755 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ docker-compose.yml /public/uploads ResponseHTTP_OK, can +result.json diff --git a/app/Console/Commands/ApiStatus.php b/app/Console/Commands/ApiStatus.php deleted file mode 100644 index 0fe8ffef..00000000 --- a/app/Console/Commands/ApiStatus.php +++ /dev/null @@ -1,102 +0,0 @@ - $collection) { - - // Loop through each item within the current collection and match it with the corresponding execution - foreach ($collection['item'] as $itemIndex => $item) { - - $execution = $executions[$itemIndex] ?? null; - $status = null; - $response_time = null; - - if ($execution && isset($execution['assertions']) && is_array($execution['assertions'])) { - // Extract the first assertion and response time if available - $assertions = $execution['assertions']; - $status = $assertions[0]['assertion'] ?? null; - $response_time = $execution['response']['responseTime'] ?? null; - } - - // Get the current date and time - $date = new DateTime("now", new DateTimeZone('Africa/Lagos')); - $last_checked = $date->format('Y-m-d h:i A'); - - - $data = [ - 'api_group' => $item['name'], - 'method' => $item['request']['method'], - 'status' => $status, - 'response_time' => $response_time, - 'last_checked' => $last_checked, - 'details' => $this->getDetails($execution) - ]; - - $url = config('app.url') . '/api/v1/api-status'; - - $response = Http::post($url, $data); - - if ($response->failed()) { - - return $this->info('Failed to send API status data: '. $response->body()); - } - - } - } - - $this->info('API status data stored successfully: ' . $response->body()); - } - - - private function getDetails($execution) - { - $response_code = $execution['response']['code'] ?? null; - $response_time = $execution['response']['responseTime'] ?? null; - - if ($response_code >= 500 || $response_code === null) { - return 'API not responding (HTTP ' . ($response_code ?? 'Unknown') . ')'; - } elseif ($response_time > 400) { - return 'High response time detected'; - } else { - return 'All tests passed'; - } - } -} diff --git a/app/Http/Controllers/Api/V1/Admin/BlogController.php b/app/Http/Controllers/Api/V1/Admin/BlogController.php index 461c2e3e..f878fbc2 100755 --- a/app/Http/Controllers/Api/V1/Admin/BlogController.php +++ b/app/Http/Controllers/Api/V1/Admin/BlogController.php @@ -65,9 +65,30 @@ public function latest(Request $request) public function index() { try{ - $blogPosts = Blog::orderBy('created_at', 'desc') - ->select('id', 'title', 'content', 'author', 'created_at', 'category', 'image_url') - ->get(); + $blogPosts = Blog::with('comments') + ->orderBy('created_at', 'desc') + ->select('id', 'title', 'content', 'author', 'created_at', 'category', 'image_url') + ->get(); + + $blogPosts = $blogPosts->map(function ($blog) { + return [ + 'id' => $blog->id, + 'title' => $blog->title, + 'content' => $blog->content, + 'published_date' => $blog->created_at?->toIso8601String(), + 'updated_date' => $blog->updated_at?->toIso8601String(), + 'author_id' => $blog->author_id, + 'category' => $blog->category, + 'image_url' => $blog->image_url, + 'comments' => $blog->comments->map(function ($comment) { + return [ + 'id' => $comment->id, + 'content' => $comment->content, + 'created_at' => $comment->created_at?->toIso8601String(), + ]; + }), + ]; + }); return response()->json([ 'data' => $blogPosts, @@ -93,17 +114,17 @@ public function create() */ public function store(Request $request) { - $author = User::where('id', Auth::id())->first(); - if(!$author){ - return response()->json(['error' => 'User Not found.'], 404); + $author = User::find(Auth::id()); + + if (!$author) { + return response()->json(['error' => 'User not found.'], 404); } - Log::error('Error creating blog post: ' . Auth::id()); $validator = Validator::make($request->all(), [ - 'title' => ['required', 'string', 'max:255'], - 'content' => ['required', 'string'], - 'image_url' => ['required', 'mimes:jpeg,png,jpg,gif,svg'], - 'category' => ['required', 'string', 'max:255'], + 'title' => 'required|string|max:255', + 'content' => 'required|string', + 'category' => 'required|string|max:255', + 'image_url' => 'required|mimes:jpeg,png,jpg,gif,svg', ]); if ($validator->fails()) { @@ -112,27 +133,47 @@ public function store(Request $request) 'status_code' => 422, ], 422); } + try { DB::beginTransaction(); - $saved = Storage::disk('public')->put('images', $request->file('image_url')); + + $savedPath = Storage::disk('public')->put('images', $request->file('image_url')); + $imageUrl = 'storage/' . $savedPath; + $blog = Blog::create([ 'title' => $request->get('title'), - 'content' => (string)$request->get('content'), - 'author' => $author->name ? $author->name : "", - 'image_url' => 'storage/'.$saved, + 'content' => $request->get('content'), + 'author' => $author->name ?? '', + 'image_url' => $imageUrl, 'category' => $request->get('category'), - 'author_id' => $author->id + 'author_id' => $author->id, ]); DB::commit(); + return response()->json([ - 'data' => $blog, - 'message' => 'Blog post created successfully.', - 'status_code' => Response::HTTP_CREATED, + 'id' => $blog->id, + 'title' => $blog->title, + 'image_url' => $blog->image_url, + 'content' => $blog->content, + 'published_date' => $blog->created_at->toIso8601String(), + 'updated_date' => $blog->updated_at->toIso8601String(), + 'author_id' => $blog->author_id, + 'category' => $blog->category, + 'comments' => $blog->comments->map(function ($comment) { + return [ + 'id' => $comment->id, + 'content' => $comment->content, + 'created_at' => $comment->created_at->toIso8601String(), + ]; + }), ], Response::HTTP_CREATED); + + } catch (\Exception $exception) { Log::error('Error creating blog post: ' . $exception->getMessage()); DB::rollBack(); + return response()->json(['error' => 'Internal server error.'], 500); } } @@ -143,11 +184,12 @@ public function store(Request $request) public function show(Request $request, string $id) { try { - $blog = Blog::find($id); + $blog = Blog::with('comments')->find($id); if(!$blog){ return response()->json([ 'error' => 'Blog not found.', + 'message' => 'Blog not found.', 'status_code' => Response::HTTP_NOT_FOUND, ], 404); } @@ -158,14 +200,21 @@ public function show(Request $request, string $id) 'category' => $blog->category, 'content' => $blog->content, 'image_url' => $blog->image_url, - 'created_at' => $blog->created_at, + 'published_date' => $blog->created_at?->toIso8601String(), + 'updated_date' => $blog->updated_at?->toIso8601String(), + 'author_id' => $blog->author_id, + 'comments' => $blog->comments->map(function ($comment) { + return [ + 'id' => $comment->id, + 'content' => $comment->content, + 'created_at' => $comment->created_at?->toIso8601String(), + ]; + }), ], - 'message' => 'Blog post fetched sucessfully.', + 'message' => 'Blog post details fetched sucessfully.', 'status_code' => Response::HTTP_OK, ], Response::HTTP_OK); } catch (Exception $exception) { - // Log::error('Error creating blog post: ' . $exception->getMessage()); - DB::rollBack(); return response()->json(['error' => 'Internal server error.'], 500); } } diff --git a/app/Http/Controllers/Api/V1/Admin/EmailTemplateController.php b/app/Http/Controllers/Api/V1/Admin/EmailTemplateController.php index da2a7d9e..55a253f2 100644 --- a/app/Http/Controllers/Api/V1/Admin/EmailTemplateController.php +++ b/app/Http/Controllers/Api/V1/Admin/EmailTemplateController.php @@ -65,6 +65,7 @@ public function index(Request $request) 'limit' => $templates->perPage(), ], 200); } + public function update(Request $request, $id) { // Validate the request data @@ -97,7 +98,6 @@ public function update(Request $request, $id) public function store(Request $request) { - // Validate request data $validatedData = $request->validate([ 'title' => 'required|string|max:255|unique:email_templates,title', @@ -108,13 +108,23 @@ public function store(Request $request) // Create email template $emailTemplate = EmailTemplate::create($validatedData); + // Format the response to match the API schema in the screenshot return response()->json([ - 'status_code' => 200, + 'data' => [ + 'id' => $emailTemplate->id, + 'name' => $emailTemplate->title, + 'subject' => '', // Add subject if applicable + 'template_body' => $emailTemplate->template, + 'placeholders' => [ + // Add placeholders if available + ], + ], 'message' => 'Email template created successfully', - 'data' => $emailTemplate - ], 200); + 'status_code' => 201 + ], 201); } + public function destroy($id) { // Find the email template by ID diff --git a/app/Http/Controllers/Api/V1/ApiStatusCheckerController.php b/app/Http/Controllers/Api/V1/ApiStatusCheckerController.php index c599a5b8..05f6d0fa 100644 --- a/app/Http/Controllers/Api/V1/ApiStatusCheckerController.php +++ b/app/Http/Controllers/Api/V1/ApiStatusCheckerController.php @@ -5,7 +5,11 @@ use DateTime; use DateTimeZone; use App\Models\ApiStatus; +use Illuminate\Support\Str; use Illuminate\Http\Request; +use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; use App\Http\Controllers\Controller; use App\Http\Requests\StoreApiStatusDataRequest; @@ -13,48 +17,156 @@ class ApiStatusCheckerController extends Controller { - public function index() - { - $api_statuses = ApiStatus::all(); - - return response()->json([ - 'status_code' => 200, - 'message' => 'Api status data returned successfully', - 'data' => $api_statuses - ]); - } + public function index() { + try { + $apiStatuses = ApiStatus::all(); - public function store(StoreApiStatusDataRequest $request) - { + $groupedStatuses = [ + 'authentication' => ['statuses' => [], 'overall_status' => ''], + 'help_center' => ['statuses' => [], 'overall_status' => ''], + 'users' => ['statuses' => [], 'overall_status' => ''], + 'jobs' => ['statuses' => [], 'overall_status' => ''], + 'products' => ['statuses' => [], 'overall_status' => ''], + 'billing' => ['statuses' => [], 'overall_status' => ''], + 'payments' => ['statuses' => [], 'overall_status' => ''], + 'languages' => ['statuses' => [], 'overall_status' => ''], + 'blog' => ['statuses' => [], 'overall_status' => ''], + 'faqs' => ['statuses' => [], 'overall_status' => ''], + 'inquiries' => ['statuses' => [], 'overall_status' => ''], + 'squeeze' => ['statuses' => [], 'overall_status' => ''], + 'cookies' => ['statuses' => [], 'overall_status' => ''], + 'email_templates' => ['statuses' => [], 'overall_status' => ''], + 'invitation' => ['statuses' => [], 'overall_status' => ''], + 'newsletter_subscription' => ['statuses' => [], 'overall_status' => ''], + 'notification' => ['statuses' => [], 'overall_status' => ''], + 'settings' => ['statuses' => [], 'overall_status' => ''], + 'other' => ['statuses' => [], 'overall_status' => ''] + ]; - $request->validated(); + foreach ($apiStatuses as $status) { + $group = $this->determineGroup($status->api_group); + $groupedStatuses[$group]['statuses'][] = $status; + } - try { - $date = new DateTime("now", new DateTimeZone('Africa/Lagos')); - $last_checked = $date->format('Y-m-d h:i A'); - - // Update the api status record - ApiStatus::updateOrCreate(['api_group' => $request->api_group], [ - 'api_group' => $request->api_group, - 'method' => $request->method, - 'status' => $request->status, - 'response_time' => $request->response_time, - 'last_checked' => $request->last_checked ?? $last_checked, - 'details' => $request->details, - ]); + foreach ($groupedStatuses as $group => $data) { + $groupedStatuses[$group]['overall_status'] = $this->calculateGroupStatus($data['statuses']); + } return response()->json([ - 'status_code' => 200, - 'message' => "Data stored successfully" + 'Status' => 200, + 'Message' => 'API status retrieved successfully', + 'data' => $groupedStatuses ], 200); - } catch (\Exception $e) { return response()->json([ - 'status_code' => 200, - 'message' => "Server error" . $e->getMessage() + 'Status' => 500, + 'Message' => "API status cannot be fetched: " . $e->getMessage() ], 500); } + } + private function calculateGroupStatus($statuses) { + if (empty($statuses)) { + return 'Unknown'; + } + + $hasDown = false; + $hasDegraded = false; + + foreach ($statuses as $status) { + if ($status->status === 'Down') { + $hasDown = true; + break; + } elseif ($status->status === 'Degraded') { + $hasDegraded = true; + } + } + + if ($hasDown) { + return 'Down'; + } elseif ($hasDegraded) { + return 'Degraded'; + } else { + return 'Operational'; + } + } + + private function determineGroup($apiGroup) { + $lowercaseGroup = strtolower($apiGroup); + + if (strpos($lowercaseGroup, 'auth') !== false || strpos($lowercaseGroup, 'login') !== false || strpos($lowercaseGroup, 'logout') !== false || strpos($lowercaseGroup, 'register') !== false || strpos($lowercaseGroup, 'password') !== false) { + return 'authentication'; + } elseif (strpos($lowercaseGroup, 'help') !== false || strpos($lowercaseGroup, 'article') !== false) { + return 'help_center'; + } elseif (strpos($lowercaseGroup, 'user') !== false) { + return 'users'; + } elseif (strpos($lowercaseGroup, 'job') !== false) { + return 'jobs'; + } elseif (strpos($lowercaseGroup, 'product') !== false || strpos($lowercaseGroup, 'categories') !== false) { + return 'products'; + } elseif (strpos($lowercaseGroup, 'billing') !== false) { + return 'billings'; + } elseif (strpos($lowercaseGroup, 'payment') !== false) { + return 'payments'; + } elseif (strpos($lowercaseGroup, 'language') !== false) { + return 'language'; + }elseif (strpos($lowercaseGroup, 'blog') !== false || strpos($lowercaseGroup, 'comments') !== false) { + return 'blog'; + } elseif (strpos($lowercaseGroup, 'faq') !== false) { + return 'faqs'; + } elseif (strpos($lowercaseGroup, 'inquiries') !== false) { + return 'inquiries'; + } elseif (strpos($lowercaseGroup, 'squeeze') !== false) { + return 'squeeze_pages'; + } elseif (strpos($lowercaseGroup, 'cookies') !== false) { + return 'cookies_pages'; + } elseif (strpos($lowercaseGroup, 'email template') !== false) { + return 'email_template'; + } elseif (strpos($lowercaseGroup, 'invitation') !== false) { + return 'invitation'; + } elseif (strpos($lowercaseGroup, 'newsletter') !== false) { + return 'newsletter_subscription'; + } elseif (strpos($lowercaseGroup, 'notification') !== false) { + return 'notification'; + } elseif (strpos($lowercaseGroup, 'setting') !== false) { + return 'settings'; + } else { + return 'other'; + } + } + + + + public function store(Request $request) + { + try { + DB::beginTransaction(); + + foreach ($request->all() as $item) { + if($item==null){ + continue; + } + $lastChecked = Carbon::createFromTimestamp($item['last_checked'])->setTimezone('Africa/Lagos')->format('Y-m-d H:i:s'); + + ApiStatus::updateOrCreate( + ['api_group' => $item['api_group']], + [ + 'id' => Str::uuid()->toString(), + 'status' => $item['status'], + 'last_checked' => $lastChecked, + 'response_time' => $item['response_time'], + 'details' => $item['details'], + ] + ); + } + + DB::commit(); + return response()->json(['message' => 'API statuses updated successfully'], 200); + } catch (\Exception $e) { + DB::rollBack(); + \Log::error('Error updating API statuses: ' . $e->getMessage()); + return response()->json(['error' => 'An error occurred while updating API statuses: ' . $e->getMessage()], 500); + } } } diff --git a/app/Http/Controllers/Api/V1/CategoryController.php b/app/Http/Controllers/Api/V1/CategoryController.php index d94fe3a1..35d5feb4 100755 --- a/app/Http/Controllers/Api/V1/CategoryController.php +++ b/app/Http/Controllers/Api/V1/CategoryController.php @@ -12,48 +12,19 @@ class CategoryController extends Controller public function index(Request $request) { try { - // Optional query parameters - $limit = $request->query('limit', 100); - $offset = $request->query('offset', 0); - $parent_id = $request->query('parent_id', null); - // Validate query parameters - $request->validate([ - 'limit' => 'integer|min:1', - 'offset' => 'integer|min:0', - 'parent_id' => 'integer|nullable', - ]); - - $categories = Category::when($parent_id, function ($query) use ($parent_id) { - return $query->where('parent_id', $parent_id); - }) - ->limit($limit) - ->offset($offset) - ->get(['id', 'name', 'description', 'slug', 'parent_id']); - - \Log::info($categories); + $categories = Category::all(); return response()->json([ 'status_code' => 200, - 'categories' => $categories, + 'message' => 'Categories returned successfully', + 'data' => $categories, ]); - } catch (\Illuminate\Validation\ValidationException $e) { - return response()->json([ - 'status_code' => 400, - 'error' => [ - 'code' => 'INVALID_QUERY_PARAMETER', - 'message' => 'The provided query parameter is invalid.', - 'details' => [ - 'invalid_parameter' => $e->validator->errors()->keys()[0], - 'reason' => $e->validator->errors()->first(), - ], - ], - ], 400); - } catch (\Exception $e) { + + }catch (\Exception $e) { return response()->json([ 'status_code' => 500, 'error' => [ - 'code' => 'INTERNAL_SERVER_ERROR', 'message' => 'An unexpected error occurred while processing your request.', 'details' => [ 'support_email' => 'support@example.com', diff --git a/app/Http/Controllers/Api/V1/HelpArticleController.php b/app/Http/Controllers/Api/V1/HelpArticleController.php index 3f675d4d..57012769 100644 --- a/app/Http/Controllers/Api/V1/HelpArticleController.php +++ b/app/Http/Controllers/Api/V1/HelpArticleController.php @@ -8,8 +8,8 @@ use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Auth; use Illuminate\Database\QueryException; -use Illuminate\Support\Facades\RateLimiter; use App\Models\User; +use Illuminate\Support\Str; class HelpArticleController extends Controller { @@ -17,17 +17,16 @@ class HelpArticleController extends Controller public function store(Request $request) { - // Ensure the user is authenticated if (!Auth::check()) { return response()->json([ 'status_code' => 401, - 'success' => false, - 'message' => 'Authentication failed' + 'message' => 'Authentication failed', + 'data' => null ], 401); } + // Validate the request data $validator = Validator::make($request->all(), [ - 'user_id' => 'required|uuid|exists:users,id', 'title' => 'required|string|max:255', 'content' => 'required|string', ]); @@ -35,128 +34,106 @@ public function store(Request $request) if ($validator->fails()) { return response()->json([ 'status_code' => 422, - 'success' => false, 'message' => 'Invalid input data.', - 'errors' => $validator->errors() + 'errors' => $validator->errors(), + 'data' => null ], 422); } try { + // Create a new help article $article = HelpArticle::create([ - 'user_id' => $request->user_id, + 'user_id' => Auth::id(), 'title' => $request->title, 'content' => $request->content, ]); return response()->json([ 'status_code' => 201, - 'success' => true, 'message' => 'Help article created successfully.', - 'data' => $article + 'data' => [ + 'id' => $article->article_id, + 'title' => $article->title, + 'content' => $article->content, + 'author' => Auth::user()->name + ] ], 201); } catch (QueryException $e) { - if ($e->getCode() === '23505') { // Unique violation error code + if ($e->getCode() === '23505') { return response()->json([ 'status_code' => 409, - 'success' => false, - 'message' => 'An article with this title already exists.' + 'message' => 'An article with this title already exists.', + 'data' => null ], 409); } return response()->json([ 'status_code' => 500, - 'success' => false, 'message' => 'Failed to create help article. Please try again later.', - 'error' => $e->getMessage() // Include error message + 'error' => $e->getMessage(), + 'data' => null ], 500); } catch (\Exception $e) { return response()->json([ 'status_code' => 500, - 'success' => false, 'message' => 'Failed to create help article. Please try again later.', - 'error' => $e->getMessage() // Include error message + 'error' => $e->getMessage(), + 'data' => null ], 500); } } public function update(Request $request, $articleId) { - // Ensure the user is authenticated - if (!Auth::check()) { - return response()->json([ - 'status_code' => 401, - 'success' => false, - 'message' => 'Authentication failed' - ], 401); - } - - // Validate the input - $validator = Validator::make($request->all(), [ - 'title' => 'nullable|string|max:255', - 'content' => 'nullable|string', - ]); - - if ($validator->fails()) { - return response()->json([ - 'status_code' => 400, - 'success' => false, - 'message' => 'Invalid input data.', - 'errors' => $validator->errors() - ], 400); - } - try { - $article = HelpArticle::find($articleId); + $article = HelpArticle::findOrFail($articleId); - if (!$article) { - return response()->json([ - 'status_code' => 404, - 'success' => false, - 'message' => 'Help article not found.' - ], 404); - } + $validator = Validator::make($request->all(), [ + 'title' => 'nullable|string|max:255', + 'content' => 'nullable|string', + ]); - // Check if the authenticated user is the author of the article - if (Auth::id() !== $article->user_id) { + if ($validator->fails()) { return response()->json([ - 'status_code' => 403, + 'status_code' => 400, 'success' => false, - 'message' => 'You do not have permission to update this article.' - ], 403); + 'message' => 'Invalid input data.', + 'errors' => $validator->errors() + ], 400); } $article->update($request->only(['title', 'content'])); + $data = [ + 'id' => $article->article_id, + 'title' => $article->title, + 'content' => $article->content, + 'author' => $article->user_id + ]; return response()->json([ 'status_code' => 200, - 'success' => true, - 'message' => 'Help article updated successfully.', - 'data' => $article - ]); - } catch (QueryException $e) { - return response()->json([ - 'status_code' => 500, - 'success' => false, - 'message' => 'Failed to update help article. Please try again later.', - 'error' => $e->getMessage() // Include error message - ], 500); + 'message' => 'Topic updated successfully', + 'data' => $data + ], 200); } catch (\Exception $e) { return response()->json([ - 'status_code' => 500, - 'success' => false, - 'message' => 'Failed to update help article. Please try again later.', - 'error' => $e->getMessage() // Include error message - ], 500); + 'status_code' => 404, + 'message' => 'Help article not found', + 'data' => null + ], 404); } } + + + public function destroy($articleId) { // Ensure the user is authenticated if (!Auth::check()) { return response()->json([ 'status_code' => 401, - 'success' => false, - 'message' => 'Authentication failed' + 'message' => 'Authentication failed', + 'data' => null ], 401); } @@ -167,8 +144,8 @@ public function destroy($articleId) if (!$article) { return response()->json([ 'status_code' => 404, - 'success' => false, - 'message' => 'Help article not found.' + 'message' => 'Help article not found.', + 'data' => null ], 404); } @@ -176,8 +153,8 @@ public function destroy($articleId) if ($article->user_id !== Auth::id()) { return response()->json([ 'status_code' => 403, - 'success' => false, - 'message' => 'You do not have permission to access this resource.' + 'message' => 'You do not have permission to access this resource.', + 'data' => null ], 403); } @@ -186,18 +163,18 @@ public function destroy($articleId) return response()->json([ 'status_code' => 200, - 'success' => true, - 'message' => 'Help article deleted successfully.' + 'message' => 'Topic deleted successfully', + 'data' => null ], 200); } catch (\Exception $e) { return response()->json([ 'status_code' => 500, - 'success' => false, 'message' => 'Failed to delete help article. Please try again later.', - 'error' => $e->getMessage() + 'data' => null ], 500); } } + public function getArticles(Request $request) { // Validate query parameters @@ -209,10 +186,8 @@ public function getArticles(Request $request) ]); try { - // Build the query $query = HelpArticle::query(); - // Apply search filter if provided if ($request->has('search')) { $query->where(function ($query) use ($request) { $query->where('title', 'like', '%' . $request->search . '%') @@ -220,29 +195,25 @@ public function getArticles(Request $request) }); } - // Apply category filter if provided if ($request->has('category')) { $query->where('category', $request->category); } - // Pagination $page = $request->get('page', 1); $size = $request->get('size', 10); $articles = $query->paginate($size, ['*'], 'page', $page); return response()->json([ 'status_code' => 200, - 'success' => true, 'message' => 'Articles retrieved successfully.', - 'data' => [ - 'topics' => $articles->items(), - 'pagination' => [ - 'page' => $articles->currentPage(), - 'size' => $articles->perPage(), - 'total_pages' => $articles->lastPage(), - 'total_items' => $articles->total() - ] - ] + 'data' => collect($articles->items())->map(function ($item) { + return [ + 'id' => $item->article_id, + 'title' => $item->title, + 'content' => $item->content, + 'author' => $item->user->name, + ]; + })->toArray() ]); } catch (\Exception $e) { return response()->json([ @@ -312,4 +283,33 @@ public function search(Request $request) ], 500); } } + + public function show($articleId) + { + try { + $article = HelpArticle::findOrFail($articleId); + + $author = User::find($article->user_id); + $authorName = $author ? $author->name : null; + + $data = [ + 'id' => $article->article_id, + 'title' => $article->title, + 'content' => $article->content, + 'author' => $authorName + ]; + + return response()->json([ + 'status_code' => 200, + 'message' => 'Request completed successfully', + 'data' => $data + ], 200); + } catch (\Exception $e) { + return response()->json([ + 'status_code' => 404, + 'message' => 'Help article not found', + 'data' => null + ], 404); + } + } } diff --git a/app/Http/Controllers/Api/V1/JobController.php b/app/Http/Controllers/Api/V1/JobController.php index 3ee47fe1..936419fb 100755 --- a/app/Http/Controllers/Api/V1/JobController.php +++ b/app/Http/Controllers/Api/V1/JobController.php @@ -7,6 +7,8 @@ use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\Validator; +use Illuminate\Database\Eloquent\ModelNotFoundException; + class JobController extends Controller { @@ -68,12 +70,12 @@ public function store(Request $request) 'title' => 'required|string', 'description' => 'required|string', 'location' => 'required|string', - 'deadline' => 'required|date', - 'salary_range' => 'required|string', - 'job_type' => 'required|string', - 'job_mode' => 'required|string', - 'experience_level' => 'nullable|string', - 'company_name' => 'nullable|string', + 'deadline' => 'nullable|date', + 'salary' => 'required|string', + 'job_type' => 'nullable|string', + 'job_mode' => 'nullable|string', + 'level' => 'required|string', + 'company' => 'required|string', 'key_responsibilities' => 'nullable|string', 'qualifications' => 'nullable|string', ]); @@ -90,19 +92,28 @@ public function store(Request $request) $validator->validated(), [ 'user_id' => auth()->id(), - 'salary' => $request->input('salary_range'), + 'salary' => $request->input('salary'), 'work_mode' => $request->input('job_mode'), - 'benefits' => $request->input('company_name'), + 'company' => $request->input('company'), + 'experience_level' => $request->input('level'), + 'job_type' => $request->input('job_type'), ] )); $responseData = $job->toArray(); - $responseData['company_name'] = $responseData['benefits']; - unset($responseData['benefits']); return response()->json([ 'success' => true, 'message' => 'Job listing created successfully.', - 'data' => $job + 'data' => [ + "id" => $job->id, + "title" => $job->title, + "description" => $job->description, + "location" => $job->location, + "salary" => $job->salary, + "level" => $job->experience_level, + "comapany" => $job->company, + "date-posted" => $job->created_at, + ] ], Response::HTTP_CREATED); } @@ -177,19 +188,47 @@ public function update(Request $request, $id) public function destroy($id) { + // Check if the authenticated user is an admin if (auth()->user()->role !== 'admin') { return response()->json([ - 'success' => false, - 'message' => 'Only admin users can delete job listings.', + 'data' => null, + 'error' => 'Only admin users can delete job listings.', + 'message' => 'Unauthorized action.', + 'status_code' => Response::HTTP_FORBIDDEN, ], Response::HTTP_FORBIDDEN); } - $job = Job::findOrFail($id); - $job->delete(); + try { + // Find the job listing by ID + $job = Job::findOrFail($id); - return response()->json([ - 'success' => true, - 'message' => 'Job listing deleted successfully.' - ], Response::HTTP_OK); + // Delete the job listing + $job->delete(); + + return response()->json([ + 'data' => null, + 'error' => null, + 'message' => 'Job listing deleted successfully.', + 'status_code' => Response::HTTP_OK, + ], Response::HTTP_OK); + + } catch (ModelNotFoundException $e) { + // Handle the case where the job listing was not found + return response()->json([ + 'data' => null, + 'error' => 'Job listing not found.', + 'message' => 'Job listing could not be found.', + 'status_code' => Response::HTTP_NOT_FOUND, + ], Response::HTTP_NOT_FOUND); + } catch (\Exception $e) { + // Handle any other errors that may occur + return response()->json([ + 'data' => null, + 'error' => 'An error occurred while deleting the job listing.', + 'message' => 'Failed to delete job listing.', + 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, + ], Response::HTTP_INTERNAL_SERVER_ERROR); + } } + } diff --git a/app/Http/Controllers/Api/V1/Organisation/OrganisationController.php b/app/Http/Controllers/Api/V1/Organisation/OrganisationController.php index 00f542a7..2fed4207 100755 --- a/app/Http/Controllers/Api/V1/Organisation/OrganisationController.php +++ b/app/Http/Controllers/Api/V1/Organisation/OrganisationController.php @@ -170,10 +170,20 @@ public function removeUser(Request $request, $org_id, $user_id) { $organisation = Organisation::findOrFail($org_id); + if (!$organisation) { + return response()->json([ + 'data' => null, + 'error' => 'Not Found', + 'message' => 'Organization not found', + 'status_code' => 404 + ], 404); + } + // Use $request->auth instead of Auth::user() if (!$request->user()->can('removeUser', $organisation)) { return response()->json([ - 'status' => 'Forbidden', + 'data' => null, + 'error' => 'Unauthorized', 'message' => 'Only admin can remove users', 'status_code' => 403 ], 403); @@ -183,8 +193,9 @@ public function removeUser(Request $request, $org_id, $user_id) if (!$user || !$organisation->users()->detach($user)) { return response()->json([ - 'status' => 'forbidden', - 'message' => 'user not found', + 'data' => null, + 'error' => 'Not Found', + 'message' => 'User not found', 'status_code' => 404 ], 404); } diff --git a/app/Http/Controllers/Api/V1/ProductController.php b/app/Http/Controllers/Api/V1/ProductController.php index 0b6da396..cf5a6884 100755 --- a/app/Http/Controllers/Api/V1/ProductController.php +++ b/app/Http/Controllers/Api/V1/ProductController.php @@ -4,18 +4,11 @@ use App\Models\OrganisationUser; use App\Http\Requests\UpdateProductRequest; -use App\Models\User; use App\Models\Product; -use Carbon\Carbon; use Illuminate\Http\Request; use App\Http\Controllers\Controller; use App\Http\Requests\CreateProductRequest; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Storage; -use Illuminate\Support\Facades\Validator; -use Illuminate\Support\Str; use App\Http\Resources\ProductResource; -use App\Models\Order; class ProductController extends Controller @@ -28,8 +21,8 @@ public function search(Request $request, $orgId) $query = Product::where('org_id', $orgId); // Apply filters based on query parameters - if ($request->has('name')) { - $query->where('name', 'like', '%' . $request->query('name') . '%'); + if ($request->has('product_name')) { + $query->where('name', 'like', '%' . $request->query('product_name') . '%'); } if ($request->has('category')) { @@ -46,30 +39,31 @@ public function search(Request $request, $orgId) $products = $query->get(); + // If no products are found, return a 404 response + if ($products->isEmpty()) { + return response()->json([ + 'type' => 'Not Found', + 'title' => 'No products found', + 'status' => 404, + 'detail' => 'No products match the search criteria.', + 'instance' => url()->current(), + ], 404); + } + // Transform the products for the response $transformedProducts = $products->map(function ($product) { return [ 'id' => $product->product_id, - 'created_at' => $product->created_at, - 'updated_at' => $product->updated_at, 'name' => $product->name, 'description' => $product->description, - 'category' => $product->category, - 'image' => $product->imageUrl ? url($product->imageUrl) : null, 'price' => $product->price, - 'cost_price' => $product->cost_price, - 'quantity' => $product->quantity, - 'size' => $product->size, - 'stock_status' => $product->quantity > 0 ? 'in stock' : 'out of stock', - 'deletedAt' => $product->deletedAt, + 'category' => $product->category, + 'created_at' => $product->created_at->toIso8601String(), + 'updated_at' => $product->updated_at->toIso8601String(), ]; }); - return response()->json([ - 'status_code' => 200, - 'message' => 'Products retrieved successfully', - 'data' => $transformedProducts - ]); + return response()->json($transformedProducts, 200); } @@ -378,4 +372,29 @@ public function destroy($org_id, $product_id) 'data' => $product ], 200); } + + public function delete($product_id) + { + + + + $product = Product::find($product_id); + + if (!$product) { + return response()->json([ + 'error' => 'Product not found', + 'message' => "The product with ID $product_id does not exist." + ], 404); + } + + + + $product->delete(); + + return response()->json([ + 'status_code' => 204, + 'message' => 'Product successfully deleted.', + // 'data' => $product + ], 204); + } } diff --git a/app/Http/Controllers/Api/V1/RoleController.php b/app/Http/Controllers/Api/V1/RoleController.php index fbea1329..af7a514e 100755 --- a/app/Http/Controllers/Api/V1/RoleController.php +++ b/app/Http/Controllers/Api/V1/RoleController.php @@ -25,17 +25,14 @@ class RoleController extends Controller /** * Store a newly created resource in storage. */ - public function store(StoreRoleRequest $request) + public function store(StoreRoleRequest $request, $orgId) { - Log::info('Incoming role creation request', $request->all()); try { DB::beginTransaction(); - $org_id = $request->organisation_id; - // Validate the organisation ID as a UUID - if (!preg_match('/^[a-f0-9-]{36}$/', $org_id)) { + if (!preg_match('/^[a-f0-9-]{36}$/', $orgId)) { return response()->json([ 'status_code' => Response::HTTP_BAD_REQUEST, 'error' => 'Invalid input', @@ -44,17 +41,17 @@ public function store(StoreRoleRequest $request) } // Check whether the organisation exists - $organisation = Organisation::find($org_id); + $organisation = Organisation::find($orgId); if (!$organisation) { return response()->json([ 'status_code' => Response::HTTP_NOT_FOUND, 'error' => 'Organisation not found', - 'message' => 'The organisation with ID ' . $org_id . ' does not exist', + 'message' => 'The organisation with ID ' . $orgId . ' does not exist', ], Response::HTTP_NOT_FOUND); } // Check for duplicate role name within the organisation - $existingRole = Role::where('org_id', $org_id)->where('name', $request->role_name)->first(); + $existingRole = Role::where('org_id', $orgId)->where('name', $request->name)->first(); if ($existingRole) { return response()->json([ 'status_code' => Response::HTTP_CONFLICT, @@ -65,22 +62,34 @@ public function store(StoreRoleRequest $request) // Creating the role $role = Role::create([ - 'name' => $request->role_name, + 'name' => $request->name, 'description' => $request->description, - 'org_id' => $org_id, + 'org_id' => $orgId, ]); // Attach the permission to the role - $role->permissions()->attach($request->permissions_id); + $role->permissions()->attach($request->permissions); + + // Retrieve all attached permissions with additional details + $permissions = $role->permissions->map(function ($permission) { + return [ + 'id' => $permission->permission_id, + 'name' => $permission->name, + ]; + }); DB::commit(); return response()->json([ - 'id' => $role->id, - 'name' => $role->name, - 'description' => $role->description, - 'message' => "Role created successfully", - 'status_code' => Response::HTTP_CREATED, + 'status_code' => 201, + 'data' => [ + 'id' => $role->id, + 'name' => $role->name, + 'description' => $role->description, + 'permissions' => $permissions, + ], + 'error' => null, + 'message' => 'Role created successfully', ], Response::HTTP_CREATED); } catch (\Exception $e) { DB::rollBack(); @@ -158,12 +167,16 @@ public function assignRole(Request $request, $org_id, $user_id) 'role' => 'required|string|exists:roles,name', ]); + if ($validator->fails()) { + return $this->apiResponse($validator->errors(), 422); + } if ($validator->fails()) { return $this->apiResponse($validator->errors(), 422); } $user = User::find($user_id); - if (!$user) return ResponseHelper::response("User not found", 404, null); + if (!$user) + return ResponseHelper::response("User not found", 404, null); if ($organisation = Organisation::find($org_id)) { if (!$organisation->users->contains($user->id)) return ResponseHelper::response("You are not authorised to perform this action", 409, null); @@ -173,22 +186,27 @@ public function assignRole(Request $request, $org_id, $user_id) return ResponseHelper::response("Roles updated successfully", 200, null); else return response()->json(['message' => 'Role update failed', 'error' => $result], 400); - } else return ResponseHelper::response("Organisation not found", 404, null); + } else + return ResponseHelper::response("Organisation not found", 404, null); } public function update(UpdateRoleRequest $request, $org_id, $role_id) { $user = auth('api')->user(); - if (!$user) return ResponseHelper::response("Authentication failed", 401, null); + if (!$user) + return ResponseHelper::response("Authentication failed", 401, null); if ($organisation = Organisation::find($org_id)) { - if (!$organisation->users->contains($user->id)) return ResponseHelper::response("You are not authorised to perform this action", 401, null); + if (!$organisation->users->contains($user->id)) + return ResponseHelper::response("You are not authorised to perform this action", 401, null); if ($role = Role::find($role_id)) { $role->update($request->only('name', 'description')); return ResponseHelper::response("Role updated successfully", 200, $role); - } else return ResponseHelper::response("Role not found", 404, null); - } else return ResponseHelper::response("Organisation not found", 404, null); + } else + return ResponseHelper::response("Role not found", 404, null); + } else + return ResponseHelper::response("Organisation not found", 404, null); } - + // To get all roles public function index($org_id) { @@ -233,7 +251,7 @@ public function show($org_id, $role_id) { try { // Validate UUID format - if (!Str::isUuid($org_id) || !is_numeric($role_id)) { + if (!Str::isUuid($org_id) || !Str::isUuid($role_id)) { Log::error('Invalid UUID or ID format', [ 'org_id' => $org_id, 'role_id' => $role_id, @@ -338,7 +356,8 @@ public function assignPermissions(Request $request, $org_id, $role_id) $payload = Validator::make($request->all(), [ 'permission_list' => 'required|array' ]); - if ($payload->fails()) return ResponseHelper::response($payload->errors(), 422, null); + if ($payload->fails()) + return ResponseHelper::response($payload->errors(), 422, null); if ($role && !$payload->fails()) { foreach ($request->permission_list as $permission => $value) { $permissionId = Permission::where('name', $permission)->value('id'); @@ -349,7 +368,8 @@ public function assignPermissions(Request $request, $org_id, $role_id) } } return ResponseHelper::response("Permissions updated successfully", 202, null); - } else return ResponseHelper::response("Role not found", 404, null); + } else + return ResponseHelper::response("Role not found", 404, null); } public function destroy(Request $request, $org_id, $role_id) diff --git a/app/Http/Controllers/Api/V1/User/UserController.php b/app/Http/Controllers/Api/V1/User/UserController.php index f8934ca1..770b7674 100755 --- a/app/Http/Controllers/Api/V1/User/UserController.php +++ b/app/Http/Controllers/Api/V1/User/UserController.php @@ -95,13 +95,8 @@ public function show(User $user) 'username' => '', 'jobTitle' => $user->profile->job_title ?? null, 'pronouns' => $user->profile->pronoun ?? null, - 'department' => null, 'email' => $user->email, 'bio' => $user->profile->bio ?? null, - 'social_links' => null, - 'language' => null, - 'region' => null, - 'timezones' => null, 'profile_pic_url' => $user->profile->avatar_url ?? null, 'deletedAt' => $user->profile->deleted_at ?? null ], @@ -138,7 +133,14 @@ public function update(Request $request, string $id) 'first_name' => 'nullable|string|max:255', 'last_name' => 'nullable|string|max:255', "email" => 'nullable|string|email|max:255|unique:users,email,' . $id, - "phone" => 'nullable|string' + "phone" => 'nullable|string', + 'pronouns' => 'nullable|string|max:255', + 'job_title' => 'nullable|string|max:255', + 'social' => 'nullable|string|max:255', + 'bio' => 'nullable|string|max:500', + 'phone_number' => 'nullable|string|max:20', + 'avatar_url' => 'nullable|string|url', + 'recovery_email' => 'nullable|string|email|max:255' ]); if ($validator->fails()) { @@ -199,7 +201,7 @@ public function destroy(string $id) if (!$user) { return response()->json([ - 'status_code' => 404, + 'statusCode' => 404, 'message' => 'User not found' ], 404); } @@ -209,7 +211,7 @@ public function destroy(string $id) if ($authUser->id !== $user->id) { if (!in_array($authUser->role, ['superAdmin', 'admin'])) { return response()->json([ - 'status_code' => 403, + 'statusCode' => 403, 'message' => 'Unauthorized to delete this user' ], 403); } @@ -218,7 +220,7 @@ public function destroy(string $id) $user->delete(); return response()->json([ - 'status_code' => 200, + 'statusCode' => 200, 'message' => 'User deleted successfully' ], 200); } diff --git a/app/Http/Controllers/Api/V1/apiupdate.php b/app/Http/Controllers/Api/V1/apiupdate.php new file mode 100644 index 00000000..e506f23c --- /dev/null +++ b/app/Http/Controllers/Api/V1/apiupdate.php @@ -0,0 +1,110 @@ + $apiGroup, + 'status' => $status, + 'last_checked' => $lastChecked, + 'response_time' => $responseTime, + 'details' => $details, + ]; + } + + // echo "Processed Data:\n"; + // print_r($processedData); + + $ch = curl_init($baseUrl . '/api/v1/api-status'); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($processedData)); + curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + if (curl_errno($ch)) { + echo "cURL Error: " . curl_error($ch) . "\n"; + } + + curl_close($ch); + + echo "Status: $httpCode\n"; + echo "Response: $response\n"; + + if ($httpCode >= 400) { + echo "Error: HTTP status code indicates an error occurred.\n"; + $responseData = json_decode($response, true); + if (json_last_error() === JSON_ERROR_NONE && isset($responseData['error'])) { + echo "Server Error Message: " . $responseData['error'] . "\n"; + } + } +} + + + + + + +function determineStatus($execution) +{ + $responseCode = $execution['response']['code'] ?? null; + $responseTime = $execution['response']['responseTime'] ?? null; + + if ($responseCode === null || $responseCode >= 500) { + return 'Down'; + } elseif ($responseTime > 400) { + return 'Degraded'; + } else { + return 'Operational'; + } +} + +function getDetails($execution) +{ + $responseCode = $execution['response']['code'] ?? null; + $responseTime = $execution['response']['responseTime'] ?? null; + + if ($responseCode === null || $responseCode >= 500) { + return 'API not responding (HTTP ' . ($responseCode ?? 'Unknown') . ')'; + } elseif ($responseTime > 400) { + return 'High response time detected'; + } else { + return 'All tests passed'; + } +} + +processJsonAndSend($baseUrl); diff --git a/app/Http/Controllers/BillingPlanController.php b/app/Http/Controllers/BillingPlanController.php index 62018f88..2a5dc9dd 100644 --- a/app/Http/Controllers/BillingPlanController.php +++ b/app/Http/Controllers/BillingPlanController.php @@ -103,7 +103,56 @@ public function edit(string $id) */ public function update(Request $request, string $id) { - // + // Validate the incoming request data + $validatedData = $request->validate([ + 'name' => 'required|string|max:255', + 'frequency' => 'required|string|in:Monthly,Yearly', + 'is_active' => 'boolean', + 'amount' => 'required|numeric|min:0', + 'description' => 'required|string', + ]); + + try { + $billingPlan = SubscriptionPlan::find($id); + // Check if the billing plan exists + if (!$billingPlan) { + return response()->json([ + 'data' => null, + 'error' => 'Billing plan not found', + 'message' => 'No billing plan found with the specified ID', + 'status_code' => 404 + ], 404); + } + + // Update the billing plan with the new data + $billingPlan->name = $validatedData['name']; + $billingPlan->duration = $validatedData['frequency']; + $billingPlan->price = $validatedData['amount']; + $billingPlan->description = $validatedData['description']; + $billingPlan->save(); // Save the changes + + // Return a success response + return response()->json([ + 'data' => [ + 'id' => $billingPlan->id, + 'name' => $billingPlan->name, + 'frequency' => $billingPlan->duration, + 'amount' => $billingPlan->price, + 'description' => $billingPlan->description, + 'created_at' => $billingPlan->created_at, + 'updated_at' => $billingPlan->updated_at, + ], + 'message' => 'Billing plan updated successfully', + 'status_code' => 200 + ], 200); + } catch (\Throwable $th) { + return response()->json([ + 'data' => null, + 'error' => 'Internal server error', + 'message' => 'An error occurred while updating the billing plan', + 'status_code' => 500 + ], 500); + } } /** @@ -111,6 +160,30 @@ public function update(Request $request, string $id) */ public function destroy(string $id) { - // + $plans = SubscriptionPlan::find($id); + + if (!$plans) { + return response()->json([ + 'status_code' => 404, + 'error' => 'Not Found', + 'message' => 'Plan not found' + ], 404); + } + + try { + $plans->delete(); + + // Return success response + return response()->json([ + 'data' => true, + 'status_code' => 200, + 'message' => 'Plan deleted successfully' + ], 200); + } catch (\Exception $e) { + return response()->json([ + 'status' => 500, + 'message' => 'Internal server error' + ], 500); + } } } diff --git a/app/Http/Controllers/UserNotificationController.php b/app/Http/Controllers/UserNotificationController.php index faaae290..1db79003 100644 --- a/app/Http/Controllers/UserNotificationController.php +++ b/app/Http/Controllers/UserNotificationController.php @@ -59,7 +59,6 @@ public function create(CreateNotificationRequest $request) // Return 201 return response()->json([ - 'status' => 'success', 'message' => 'Notification created successfully', 'status_code' => 201, 'data' => [ @@ -115,9 +114,11 @@ public function getByUser(Request $request) $isRead = $notification->pivot->status === 'read'; return [ 'id' => $notification->id, - 'message' => $notification->message, + 'user_id' => auth()->id(), 'is_read' => $isRead, + 'message' => $notification->message, 'created_at' => $notification->created_at, + 'updated_at' => $notification->updated_at, ]; }); @@ -125,8 +126,7 @@ public function getByUser(Request $request) $status = $isRead === 'false' ? 'Unread ' : ''; return response()->json([ - 'status' => 'success', - 'message' => "{$status}Notifications retrieved successfully", + 'message' => "{$status}Notification retrieved successfully", 'status_code' => Response::HTTP_OK, 'data' => [ 'total_notification_count' => $totalNotificationsCount, @@ -182,36 +182,40 @@ public function update(Request $request, $notificationId) /** * Remove the specified resource from storage. */ - public function destroy() + public function destroy(Request $request) { try { - - UserNotification::query()->where([ - ['user_id', auth()->id()], - ['status', 'unread'] - ])->update([ - 'status' => 'read' - ]); - + // Check if the user is authenticated + $user = Auth::user(); + + // Update the status of all notifications (read or unread) for the authenticated user to 'read' + UserNotification::query() + ->where('user_id', $user->id) + ->update(['status' => 'read']); + + // Return a success response return response()->json([ - 'status' => 'success', - 'message' => 'notifications cleared successfully', - 'status_code' => Response::HTTP_OK, - 'data' => UserNotification::query()->where('user_id', auth()->id())->get(), + 'data' => 'All notifications have been cleared.', + 'message' => 'All notifications cleared successfully', + 'status_code' => Response::HTTP_OK ], Response::HTTP_OK); } catch (ModelNotFoundException $exception) { + // Handle case where notifications are not found return response()->json([ - 'status' => 'error', - 'message' => 'notifications not found', - 'status_code' => Response::HTTP_NOT_FOUND, + 'data' => null, + 'error' => 'Notifications not found', + 'message' => 'No notifications found to clear', + 'status_code' => Response::HTTP_NOT_FOUND ], Response::HTTP_NOT_FOUND); } catch (\Exception $exception) { Log::error($exception->getMessage()); return response()->json([ - 'status' => 'error', - 'message' => 'something went wrong', - 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR, + 'data' => null, + 'error' => 'Internal Server Error', + 'message' => 'Something went wrong while clearing notifications', + 'status_code' => Response::HTTP_INTERNAL_SERVER_ERROR ], Response::HTTP_INTERNAL_SERVER_ERROR); } } + } diff --git a/app/Http/Requests/StoreRoleRequest.php b/app/Http/Requests/StoreRoleRequest.php index e656d60f..565fc216 100755 --- a/app/Http/Requests/StoreRoleRequest.php +++ b/app/Http/Requests/StoreRoleRequest.php @@ -23,26 +23,33 @@ public function authorize(): bool public function rules(): array { return [ - 'role_name' => [ + 'name' => [ 'required', 'string', 'max:255' ], - 'organisation_id' => [ - 'required', + 'description' => [ + 'nullable', 'string', 'max:255', - 'exists:organisations,org_id', - Rule::unique('roles', 'org_id')->where('name', $this->input('role_name')) ], - 'permissions_id' => 'required', + 'permissions' => [ + 'required', + 'array', + ], + 'permissions.*' => [ + 'uuid', + 'exists:permissions,id', + ], ]; } public function messages() { return [ - 'organisation_id.unique'=> "'".$this->input('role_name')."' role has already been created for the organisation", + 'permissions.array' => 'Permissions must be provided as an array.', + 'permissions.*.uuid' => 'Each permission ID must be a valid UUID.', + 'permissions.*.exists' => 'Each permission ID must be valid.', ]; } } diff --git a/app/Models/Blog.php b/app/Models/Blog.php index 187a84d2..22c2dcb7 100755 --- a/app/Models/Blog.php +++ b/app/Models/Blog.php @@ -29,4 +29,9 @@ class Blog extends Model * @var string */ protected $keyType = 'string'; + + public function comments(): HasMany + { + return $this->hasMany(Comment::class); + } } diff --git a/app/Models/HelpArticle.php b/app/Models/HelpArticle.php index ffe97fb4..5278868a 100644 --- a/app/Models/HelpArticle.php +++ b/app/Models/HelpArticle.php @@ -2,13 +2,13 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Concerns\HasUuids; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; -use Illuminate\Support\Str; class HelpArticle extends Model { - use HasFactory; + use HasFactory, HasUuids; // Specify the table associated with the model protected $table = 'articles'; @@ -25,18 +25,6 @@ class HelpArticle extends Model // Define the fillable fields protected $fillable = ['user_id', 'title', 'content']; - // Define the boot method to set UUID on creation - protected static function boot() - { - parent::boot(); - - static::creating(function ($model) { - if (empty($model->article_id)) { - $model->article_id = (string) Str::uuid(); - } - }); - } - // Define relationship with User model public function user() { diff --git a/app/Models/Job.php b/app/Models/Job.php index 6bb7ab42..31eb9d21 100755 --- a/app/Models/Job.php +++ b/app/Models/Job.php @@ -22,7 +22,8 @@ class Job extends Model 'deadline', 'key_responsibilities', 'qualifications', - 'user_id' + 'user_id', + 'company' ]; diff --git a/app/Models/Permission.php b/app/Models/Permission.php index a7bbd24a..caa50ca3 100755 --- a/app/Models/Permission.php +++ b/app/Models/Permission.php @@ -2,18 +2,20 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Concerns\HasUuids; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Permission extends Model { - use HasFactory; + use HasFactory, HasUuids; protected $fillable = ['name']; public function roles() { - return $this->belongsToMany(Role::class, 'roles_permissions'); + return $this->belongsToMany(Role::class, 'roles_permissions', 'permission_id', 'role_id') + ->using(RolePermission::class); } public function users() diff --git a/app/Models/Role.php b/app/Models/Role.php index b83e61b6..2be1808e 100755 --- a/app/Models/Role.php +++ b/app/Models/Role.php @@ -2,18 +2,20 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Concerns\HasUuids; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Role extends Model { - use HasFactory; + use HasFactory, HasUuids; protected $fillable = ['name', 'description', 'org_id', 'is_active', 'is_default']; public function permissions() { - return $this->belongsToMany(Permission::class, 'roles_permissions', 'role_id', 'permission_id'); + return $this->belongsToMany(Permission::class, 'roles_permissions', 'role_id', 'permission_id') + ->using(RolePermission::class); } public function users() diff --git a/app/Models/RolePermission.php b/app/Models/RolePermission.php new file mode 100644 index 00000000..2defd7f9 --- /dev/null +++ b/app/Models/RolePermission.php @@ -0,0 +1,12 @@ +=8.1", + "php": ">=8.2", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<5.4", + "symfony/dependency-injection": "<6.4", "symfony/service-contracts": "<2.5" }, "provide": { @@ -5469,13 +5420,13 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/error-handler": "^5.4|^6.0|^7.0", - "symfony/expression-language": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^5.4|^6.0|^7.0" + "symfony/stopwatch": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -5503,7 +5454,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.8" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.1.1" }, "funding": [ { @@ -5519,7 +5470,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -6956,20 +6907,20 @@ }, { "name": "symfony/string", - "version": "v6.4.10", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "ccf9b30251719567bfd46494138327522b9a9446" + "reference": "ea272a882be7f20cad58d5d78c215001617b7f07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ccf9b30251719567bfd46494138327522b9a9446", - "reference": "ccf9b30251719567bfd46494138327522b9a9446", + "url": "https://api.github.com/repos/symfony/string/zipball/ea272a882be7f20cad58d5d78c215001617b7f07", + "reference": "ea272a882be7f20cad58d5d78c215001617b7f07", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", @@ -6979,11 +6930,12 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0|^7.0", - "symfony/http-client": "^5.4|^6.0|^7.0", - "symfony/intl": "^6.2|^7.0", + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^5.4|^6.0|^7.0" + "symfony/var-exporter": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -7022,7 +6974,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.10" + "source": "https://github.com/symfony/string/tree/v7.1.3" }, "funding": [ { @@ -7038,7 +6990,7 @@ "type": "tidelift" } ], - "time": "2024-07-22T10:21:14+00:00" + "time": "2024-07-22T10:25:37+00:00" }, { "name": "symfony/translation", @@ -8598,32 +8550,32 @@ }, { "name": "phpunit/php-code-coverage", - "version": "10.1.15", + "version": "10.1.16", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae" + "reference": "7e308268858ed6baedc8704a304727d20bc07c77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", - "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.18 || ^5.0", + "nikic/php-parser": "^4.19.1 || ^5.1.0", "php": ">=8.1", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-text-template": "^3.0", - "sebastian/code-unit-reverse-lookup": "^3.0", - "sebastian/complexity": "^3.0", - "sebastian/environment": "^6.0", - "sebastian/lines-of-code": "^2.0", - "sebastian/version": "^4.0", - "theseer/tokenizer": "^1.2.0" + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-text-template": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^3.0.0", + "sebastian/complexity": "^3.2.0", + "sebastian/environment": "^6.1.0", + "sebastian/lines-of-code": "^2.0.2", + "sebastian/version": "^4.0.1", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { "phpunit/phpunit": "^10.1" @@ -8635,7 +8587,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.1-dev" + "dev-main": "10.1.x-dev" } }, "autoload": { @@ -8664,7 +8616,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.15" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" }, "funding": [ { @@ -8672,7 +8624,7 @@ "type": "github" } ], - "time": "2024-06-29T08:25:15+00:00" + "time": "2024-08-22T04:31:57+00:00" }, { "name": "phpunit/php-file-iterator", @@ -8919,16 +8871,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.29", + "version": "10.5.30", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "8e9e80872b4e8064401788ee8a32d40b4455318f" + "reference": "b15524febac0153876b4ba9aab3326c2ee94c897" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8e9e80872b4e8064401788ee8a32d40b4455318f", - "reference": "8e9e80872b4e8064401788ee8a32d40b4455318f", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b15524febac0153876b4ba9aab3326c2ee94c897", + "reference": "b15524febac0153876b4ba9aab3326c2ee94c897", "shasum": "" }, "require": { @@ -8949,7 +8901,7 @@ "phpunit/php-timer": "^6.0.0", "sebastian/cli-parser": "^2.0.1", "sebastian/code-unit": "^2.0.0", - "sebastian/comparator": "^5.0.1", + "sebastian/comparator": "^5.0.2", "sebastian/diff": "^5.1.1", "sebastian/environment": "^6.1.0", "sebastian/exporter": "^5.1.2", @@ -9000,7 +8952,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.29" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.30" }, "funding": [ { @@ -9016,7 +8968,7 @@ "type": "tidelift" } ], - "time": "2024-07-30T11:08:00+00:00" + "time": "2024-08-13T06:09:37+00:00" }, { "name": "sebastian/cli-parser", @@ -9188,16 +9140,16 @@ }, { "name": "sebastian/comparator", - "version": "5.0.1", + "version": "5.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2db5010a484d53ebf536087a70b4a5423c102372" + "reference": "2d3e04c3b4c1e84a5e7382221ad8883c8fbc4f53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", - "reference": "2db5010a484d53ebf536087a70b4a5423c102372", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2d3e04c3b4c1e84a5e7382221ad8883c8fbc4f53", + "reference": "2d3e04c3b4c1e84a5e7382221ad8883c8fbc4f53", "shasum": "" }, "require": { @@ -9208,7 +9160,7 @@ "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^10.3" + "phpunit/phpunit": "^10.4" }, "type": "library", "extra": { @@ -9253,7 +9205,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.2" }, "funding": [ { @@ -9261,7 +9213,7 @@ "type": "github" } ], - "time": "2023-08-14T13:18:12+00:00" + "time": "2024-08-12T06:03:08+00:00" }, { "name": "sebastian/complexity", @@ -10485,26 +10437,25 @@ }, { "name": "symfony/var-exporter", - "version": "v6.4.9", + "version": "v7.1.2", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "f9a060622e0d93777b7f8687ec4860191e16802e" + "reference": "b80a669a2264609f07f1667f891dbfca25eba44c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/f9a060622e0d93777b7f8687ec4860191e16802e", - "reference": "f9a060622e0d93777b7f8687ec4860191e16802e", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/b80a669a2264609f07f1667f891dbfca25eba44c", + "reference": "b80a669a2264609f07f1667f891dbfca25eba44c", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3" + "php": ">=8.2" }, "require-dev": { "symfony/property-access": "^6.4|^7.0", "symfony/serializer": "^6.4|^7.0", - "symfony/var-dumper": "^5.4|^6.0|^7.0" + "symfony/var-dumper": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -10542,7 +10493,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v6.4.9" + "source": "https://github.com/symfony/var-exporter/tree/v7.1.2" }, "funding": [ { @@ -10558,32 +10509,31 @@ "type": "tidelift" } ], - "time": "2024-06-24T15:53:56+00:00" + "time": "2024-06-28T08:00:31+00:00" }, { "name": "symfony/yaml", - "version": "v6.4.8", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "52903de178d542850f6f341ba92995d3d63e60c9" + "reference": "fa34c77015aa6720469db7003567b9f772492bf2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/52903de178d542850f6f341ba92995d3d63e60c9", - "reference": "52903de178d542850f6f341ba92995d3d63e60c9", + "url": "https://api.github.com/repos/symfony/yaml/zipball/fa34c77015aa6720469db7003567b9f772492bf2", + "reference": "fa34c77015aa6720469db7003567b9f772492bf2", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.2", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<5.4" + "symfony/console": "<6.4" }, "require-dev": { - "symfony/console": "^5.4|^6.0|^7.0" + "symfony/console": "^6.4|^7.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -10614,7 +10564,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.4.8" + "source": "https://github.com/symfony/yaml/tree/v7.1.1" }, "funding": [ { @@ -10630,7 +10580,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "theseer/tokenizer", diff --git a/database/factories/JobFactory.php b/database/factories/JobFactory.php index b41b269e..3f52fbf5 100755 --- a/database/factories/JobFactory.php +++ b/database/factories/JobFactory.php @@ -30,7 +30,7 @@ public function definition(): array 'location' => $this->faker->city, 'salary' => $this->faker->numberBetween(30000, 150000) . ' per year', 'job_type' => $this->faker->randomElement(['Full-time', 'Part-time', 'Contract']), - 'company_name' => $this->faker->company, + 'company' => $this->faker->company, 'user_id' => $user->id, // 'organisation_id' => $organisation->org_id, ]; diff --git a/database/migrations/2024_07_21_034529_create_jobs_table.php b/database/migrations/2024_07_21_034529_create_jobs_table.php index fa222436..eed185df 100755 --- a/database/migrations/2024_07_21_034529_create_jobs_table.php +++ b/database/migrations/2024_07_21_034529_create_jobs_table.php @@ -16,8 +16,8 @@ public function up(): void $table->string('title'); $table->text('description'); $table->string('location'); - $table->string('job_type'); - $table->string('company_name')->nullable(); + $table->string('job_type')->nullable(); + $table->string('company')->nullable(); $table->timestamps(); }); } diff --git a/database/migrations/2024_07_21_121534_create_permissions_table.php b/database/migrations/2024_07_21_121534_create_permissions_table.php index fba8bb72..4b89d38f 100755 --- a/database/migrations/2024_07_21_121534_create_permissions_table.php +++ b/database/migrations/2024_07_21_121534_create_permissions_table.php @@ -12,7 +12,7 @@ public function up(): void { Schema::create('permissions', function (Blueprint $table) { - $table->id(); + $table->uuid('id')->primary(); $table->string('name'); $table->timestamp('deleted_at')->nullable(); $table->timestamps(); diff --git a/database/migrations/2024_07_21_121543_create_roles_table.php b/database/migrations/2024_07_21_121543_create_roles_table.php index 1fbea6b5..4265db25 100755 --- a/database/migrations/2024_07_21_121543_create_roles_table.php +++ b/database/migrations/2024_07_21_121543_create_roles_table.php @@ -12,7 +12,7 @@ public function up(): void { Schema::create('roles', function (Blueprint $table) { - $table->id(); + $table->uuid('id')->primary(); $table->string('name'); $table->text('description')->nullable(); $table->foreignUuid('org_id')->references('org_id')->on('organisations')->constrained()->onDelete('cascade')->onUpdate('cascade'); diff --git a/database/migrations/2024_07_21_121928_create_roles_permissions_table.php b/database/migrations/2024_07_21_121928_create_roles_permissions_table.php index cead8d4e..58878a64 100755 --- a/database/migrations/2024_07_21_121928_create_roles_permissions_table.php +++ b/database/migrations/2024_07_21_121928_create_roles_permissions_table.php @@ -12,9 +12,9 @@ public function up(): void { Schema::create('roles_permissions', function (Blueprint $table) { - $table->id(); - $table->foreignId('role_id')->constrained('roles', 'id')->cascadeOnDelete()->cascadeOnUpdate(); - $table->foreignId('permission_id')->constrained('permissions', 'id')->cascadeOnDelete() + $table->uuid('id')->primary(); + $table->foreignUuid('role_id')->constrained('roles', 'id')->cascadeOnDelete()->cascadeOnUpdate(); + $table->foreignUuid('permission_id')->constrained('permissions', 'id')->cascadeOnDelete() ->cascadeOnUpdate(); $table->timestamps(); }); diff --git a/database/migrations/2024_07_21_123053_create_users_permissions_table.php b/database/migrations/2024_07_21_123053_create_users_permissions_table.php index ee4b7f5b..2976e70b 100755 --- a/database/migrations/2024_07_21_123053_create_users_permissions_table.php +++ b/database/migrations/2024_07_21_123053_create_users_permissions_table.php @@ -14,7 +14,7 @@ public function up(): void Schema::create('users_permissions', function (Blueprint $table) { $table->id(); $table->foreignUuid('user_id')->constrained('users', 'id')->cascadeOnDelete()->cascadeOnUpdate(); - $table->foreignId('permission_id')->constrained('permissions', 'id')->cascadeOnDelete()->cascadeOnUpdate(); + $table->foreignUuid('permission_id')->constrained('permissions', 'id')->cascadeOnDelete()->cascadeOnUpdate(); $table->timestamps(); }); } diff --git a/database/migrations/2024_07_21_123058_create_users_roles_table.php b/database/migrations/2024_07_21_123058_create_users_roles_table.php index 72e1bc32..5bb5b2b8 100755 --- a/database/migrations/2024_07_21_123058_create_users_roles_table.php +++ b/database/migrations/2024_07_21_123058_create_users_roles_table.php @@ -14,7 +14,7 @@ public function up(): void Schema::create('users_roles', function (Blueprint $table) { $table->id(); $table->foreignUuid('user_id')->constrained('users', 'id')->cascadeOnDelete()->cascadeOnUpdate(); - $table->foreignId('role_id')->constrained('roles', 'id')->cascadeOnDelete()->cascadeOnUpdate(); + $table->foreignUuid('role_id')->constrained('roles', 'id')->cascadeOnDelete()->cascadeOnUpdate(); $table->timestamps(); }); } diff --git a/database/migrations/2024_08_07_131402_alter_timezones_table_rename_and_add_columns.php b/database/migrations/2024_08_07_131402_alter_timezones_table_rename_and_add_columns.php index 041b49c2..b7e02bbc 100644 --- a/database/migrations/2024_08_07_131402_alter_timezones_table_rename_and_add_columns.php +++ b/database/migrations/2024_08_07_131402_alter_timezones_table_rename_and_add_columns.php @@ -14,7 +14,7 @@ public function up(): void Schema::table('timezones', function (Blueprint $table) { $table->renameColumn('name', 'timezone'); $table->renameColumn('offset', 'gmtoffset'); - $table->string('description')->after('gmtoffset'); + $table->string('description'); }); } diff --git a/routes/api.php b/routes/api.php index 74f34656..f7823c82 100755 --- a/routes/api.php +++ b/routes/api.php @@ -110,8 +110,11 @@ Route::get('/products/search', [ProductController::class, 'search']); Route::get('/products', [ProductController::class, 'index']); Route::get('/products/{product_id}', [ProductController::class, 'show']); + Route::delete('/products/{product_id}', [ProductController::class, 'delete']); + Route::get('/billing-plans', [BillingPlanController::class, 'index']); Route::get('/billing-plans/{id}', [BillingPlanController::class, 'getBillingPlan']); + Route::put('/billing-plans/{id}', [BillingPlanController::class, 'update']); Route::get('/payments/paystack/{organisation_id}/verify/{id}', [PaymentController::class, 'handlePaystackCallback']); Route::get('/payments/flutterwave/{organisation_id}/verify/{id}', [PaymentController::class, 'handleFlutterwaveCallback']); Route::post('/languages', [LanguageController::class, 'create']); @@ -164,6 +167,7 @@ // Help Articles Route::post('/help-center/topics', [HelpArticleController::class, 'store']); + Route::get('/help-center/topics/{articleId}', [HelpArticleController::class, 'show']); Route::patch('/help-center/topics/{articleId}', [HelpArticleController::class, 'update']); Route::delete('/help-center/topics/{articleId}', [HelpArticleController::class, 'destroy']); Route::get('/help-center/topics', [HelpArticleController::class, 'getArticles']); @@ -181,12 +185,12 @@ Route::patch('/email-templates/{id}', [EmailTemplateController::class, 'update']); Route::delete('/email-templates/{id}', [EmailTemplateController::class, 'destroy']); }); - Route::middleware(['auth:api', 'admin'])->group(function () { - + Route::middleware(['auth:api', 'admin'])->group(function () { // Dashboard Route::get('/users-list', [AdminDashboardController::class, 'getUsers']); }); + Route::post('/email-requests', [SendEmailController::class, 'createEmailRequest']); @@ -243,7 +247,7 @@ Route::patch('/accounts/deactivate', [AccountController::class, 'deactivate']); // Roles - Route::post('/organisations/{org_id}/roles', [RoleController::class, 'store']); + Route::post('/organisations/{orgId}/roles', [RoleController::class, 'store']); Route::put('/organisations/{org_id}/roles/{role_id}', [RoleController::class, 'update']); Route::put('/organisations/{org_id}/roles/{role_id}/disable', [RoleController::class, 'disableRole']); Route::get('/organisations/{org_id}/roles', [RoleController::class, 'index']); @@ -266,8 +270,8 @@ - Route::get('/notification-settings', [NotificationSettingController::class, 'show']); - Route::patch('/notification-settings', [NotificationSettingController::class, 'update']); + Route::get('/settings/notification-settings', [NotificationSettingController::class, 'show']); + Route::patch('/settings/notification-settings', [NotificationSettingController::class, 'update']); }); Route::get('/notification-settings', [NotificationSettingController::class, 'show']); @@ -342,7 +346,7 @@ // quest - Route::get('/quests/{id}/messages', [QuestController::class, 'getQuestMessages']); + // Route::get('/quests/{id}/messages', [QuestController::class, 'getQuestMessages']); Route::post('/squeeze-user', [SqueezePageUserController::class, 'store']); diff --git a/send-postman-api-tests-results.php b/send-postman-api-tests-results.php new file mode 100644 index 00000000..3ae3b217 --- /dev/null +++ b/send-postman-api-tests-results.php @@ -0,0 +1,101 @@ + $apiGroup, + 'status' => $status, + 'last_checked' => $lastChecked, + 'response_time' => $responseTime, + 'details' => $details, + ]; + } + + // echo "Processed Data:\n"; + // print_r($processedData); + + $ch = curl_init($baseUrl . '/api/v1/api-status/update'); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($processedData)); + curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + if (curl_errno($ch)) { + echo "cURL Error: " . curl_error($ch) . "\n"; + } + + curl_close($ch); + + echo "Status: $httpCode\n"; + echo "Response: $response\n"; +} + + + + + +function determineStatus($execution) +{ + $responseCode = $execution['response']['code'] ?? null; + $responseTime = $execution['response']['responseTime'] ?? null; + + if ($responseCode === null || $responseCode >= 500) { + return 'Down'; + } elseif ($responseTime > 400) { + return 'Degraded'; + } else { + return 'Operational'; + } +} + +function getDetails($execution) +{ + $responseCode = $execution['response']['code'] ?? null; + $responseTime = $execution['response']['responseTime'] ?? null; + + if ($responseCode === null || $responseCode >= 500) { + return 'API not responding (HTTP ' . ($responseCode ?? 'Unknown') . ')'; + } elseif ($responseTime > 400) { + return 'High response time detected'; + } else { + return 'All tests passed'; + } +} + +processJsonAndSend($baseUrl); \ No newline at end of file diff --git a/tests/Feature/BillingPlanControllerTest.php b/tests/Feature/BillingPlanControllerTest.php new file mode 100644 index 00000000..44a39d4b --- /dev/null +++ b/tests/Feature/BillingPlanControllerTest.php @@ -0,0 +1,61 @@ +create([ + 'name' => 'Basic Plan', + 'duration' => 'Monthly', + 'price' => 10000, // stored as kobo + 'description' => 'A basic plan' + ]); + + // Define the update data + $updateData = [ + 'name' => 'Updated Plan', + 'frequency' => 'Yearly', + // 'is_active' => false, + 'amount' => 20000, // updated price in kobo + 'description' => 'An updated plan description', + ]; + + // Act: Send a PUT request to update the billing plan + $response = $this->putJson("/api/v1/billing-plans/{$plan->id}", $updateData); + + // Assert: Check the response status and structure + $response->assertStatus(200) + ->assertJsonStructure([ + 'data' => [ + 'id', + 'name', + 'frequency', + 'amount', + 'description', + 'created_at', + 'updated_at', + ], + 'message', + 'status_code', + ]); + + // Assert: Check that the database has been updated + $this->assertDatabaseHas('subscription_plans', [ + 'id' => $plan->id, + 'name' => 'Updated Plan', + 'duration' => 'Yearly', + 'price' => 20000, + 'description' => 'An updated plan description', + ]); + } +} diff --git a/tests/Feature/BlogControllerTest.php b/tests/Feature/BlogControllerTest.php index f95b7ec5..96a8e820 100644 --- a/tests/Feature/BlogControllerTest.php +++ b/tests/Feature/BlogControllerTest.php @@ -171,6 +171,7 @@ public function test_non_superadmin_cannot_delete_blog() ->deleteJson("/api/v1/blogs/{$blog->id}") ->assertStatus(401); // Forbidden } + public function test_blog_creation_endpoint_is_protected() { $response = $this->postJson('/api/v1/blogs', [ @@ -188,29 +189,40 @@ public function test_admin_can_create_blog_post() $image = UploadedFile::fake()->image('image1.jpg'); $path = Storage::putFile('public/images', $image); - $imageUrl = str_replace('public/', 'storage/', $path); + $imageUrl = 'storage/' . str_replace('public/', '', $path); $response = $this->withHeaders(['Authorization' => "Bearer $token"])->postJson('/api/v1/blogs', [ 'title' => 'Test Blog Post', 'content' => 'This is a test blog post content.', - 'author' => $admin->name, 'author_id' => $admin->id, 'image_url' => $image, 'category' => 'Example 2', ]); $response->assertStatus(201) + ->assertJsonStructure([ + 'id', + 'title', + 'image_url', + 'content', + 'published_date', + 'updated_date', + 'author_id', + 'category', + 'comments' + ]) ->assertJson([ - 'message' => 'Blog post created successfully.', - 'status_code' => 201, + 'title' => 'Test Blog Post', + 'content' => 'This is a test blog post content.', + 'author_id' => $admin->id, + 'category' => 'Example 2', ]); $this->assertDatabaseHas('blogs', [ 'title' => 'Test Blog Post', 'content' => 'This is a test blog post content.', - 'author' => $admin->name, 'author_id' => $admin->id, - 'image_url' => $imageUrl, + 'image_url' => $imageUrl, // Use the full URL here 'category' => 'Example 2', ]); diff --git a/tests/Feature/EmailTemplateControllerTest.php b/tests/Feature/EmailTemplateControllerTest.php index c1d02e3e..5db0a48d 100644 --- a/tests/Feature/EmailTemplateControllerTest.php +++ b/tests/Feature/EmailTemplateControllerTest.php @@ -248,10 +248,14 @@ public function it_creates_an_email_template_successfully() 'status' => true ]); - $response->assertStatus(200) + $response->assertStatus(201) ->assertJson([ - 'status_code' => 200, + 'status_code' => 201, 'message' => 'Email template created successfully', + 'data' => [ + 'name' => 'Welcome Email', + 'template_body' => '
Hello, welcome to our service!
', + ], ]); $this->assertDatabaseHas('email_templates', [ @@ -261,6 +265,7 @@ public function it_creates_an_email_template_successfully() ]); } + public function it_requires_title_to_create_email_template() { [$admin, $token] = $this->getAuthenticatedUser('super-admin'); diff --git a/tests/Feature/JobControllerTest.php b/tests/Feature/JobControllerTest.php index b8625431..6ad289ff 100644 --- a/tests/Feature/JobControllerTest.php +++ b/tests/Feature/JobControllerTest.php @@ -51,12 +51,11 @@ public function test_store_creates_new_job_as_admin() 'title' => 'Software Engineer', 'description' => 'Develop amazing software', 'location' => 'New York', - 'salary_range' => '100000', - 'job_type' => 'Full-time', + 'salary' => '100000', 'job_mode' => 'Remote', 'deadline' => '2023-12-31', - 'company_name' => 'Corp insurance', - 'experience_level' => 'Mid-level', + 'company' => 'Corp insurance', + 'level' => 'Mid-level', 'key_responsibilities' => 'Develop and maintain software', 'qualifications' => 'Bachelor\'s degree in Computer Science' ]; @@ -68,7 +67,7 @@ public function test_store_creates_new_job_as_admin() ->assertJsonStructure([ 'success', 'message', - 'data' => ['id', 'title', 'description', 'location', 'salary', 'job_type', 'work_mode', 'deadline', 'benefits', 'experience_level', 'key_responsibilities', 'qualifications', 'created_at', 'updated_at'] + 'data' => ['id', 'title', 'description', 'location', 'salary', 'level'] ]); $this->assertDatabaseHas('jobs', [ @@ -76,13 +75,6 @@ public function test_store_creates_new_job_as_admin() 'description' => 'Develop amazing software', 'location' => 'New York', 'salary' => '100000', - 'job_type' => 'Full-time', - 'work_mode' => 'Remote', - 'deadline' => '2023-12-31', - 'benefits' => 'Corp insurance', - 'experience_level' => 'Mid-level', - 'key_responsibilities' => 'Develop and maintain software', - 'qualifications' => 'Bachelor\'s degree in Computer Science' ]); } @@ -158,20 +150,23 @@ public function test_update_fails_for_non_admin_user() } public function test_destroy_deletes_job_as_admin() - { - $job = Job::factory()->create(); +{ + $job = Job::factory()->create(); - $response = $this->withHeaders(['Authorization' => "Bearer $this->adminToken"]) - ->deleteJson("/api/v1/jobs/{$job->id}"); + $response = $this->withHeaders(['Authorization' => "Bearer $this->adminToken"]) + ->deleteJson("/api/v1/jobs/{$job->id}"); - $response->assertStatus(200) - ->assertJson([ - 'success' => true, - 'message' => 'Job listing deleted successfully.' - ]); + $response->assertStatus(200) + ->assertJson([ + 'data' => null, + 'error' => null, + 'message' => 'Job listing deleted successfully.', + 'status_code' => 200 + ]); + + $this->assertDatabaseMissing('jobs', ['id' => $job->id]); +} - $this->assertDatabaseMissing('jobs', ['id' => $job->id]); - } public function test_destroy_fails_for_non_admin_user() { diff --git a/tests/Feature/Notifications/CreateNotificationTest.php b/tests/Feature/Notifications/CreateNotificationTest.php index 0d805a9b..9ffb8cc5 100644 --- a/tests/Feature/Notifications/CreateNotificationTest.php +++ b/tests/Feature/Notifications/CreateNotificationTest.php @@ -46,7 +46,6 @@ public function test_should_create_new_notification() // Assert the response status and structure $response->assertStatus(201) ->assertJsonStructure([ - 'status', 'message', 'status_code', 'data' => [ diff --git a/tests/Feature/Notifications/GetNotificationsTest.php b/tests/Feature/Notifications/GetNotificationsTest.php index 45d5c48a..6daa631e 100644 --- a/tests/Feature/Notifications/GetNotificationsTest.php +++ b/tests/Feature/Notifications/GetNotificationsTest.php @@ -47,7 +47,6 @@ public function test_get_all_unread_users_notifications() // Assert the response status and structure $response->assertStatus(200) ->assertJsonStructure([ - 'status', 'message', 'status_code', 'data' => [ @@ -86,7 +85,6 @@ public function test_get_all_users_notifications() // Assert the response status and structure $response->assertStatus(200) ->assertJsonStructure([ - 'status', 'message', 'status_code', 'data' => [ diff --git a/tests/Feature/OrganisationRemoveUserTest.php b/tests/Feature/OrganisationRemoveUserTest.php index 0f89b887..e6e4d00b 100755 --- a/tests/Feature/OrganisationRemoveUserTest.php +++ b/tests/Feature/OrganisationRemoveUserTest.php @@ -46,7 +46,7 @@ public function it_test_unauthorized_user_cannot_remove_user() $response->assertStatus(403) ->assertJson([ - 'status' => 'Forbidden', + 'error' => 'Unauthorized', 'message' => 'Only admin can remove users', 'status_code' => 403, ]); diff --git a/tests/Feature/OrganisationRoleTest.php b/tests/Feature/OrganisationRoleTest.php index cecae281..3228173d 100644 --- a/tests/Feature/OrganisationRoleTest.php +++ b/tests/Feature/OrganisationRoleTest.php @@ -94,7 +94,7 @@ public function test_get_role_by_org_and_role_id_not_found() // Send request for a non-existent role $response = $this->withHeaders([ 'Authorization' => "Bearer $token" - ])->getJson("/api/v1/organisation/{$organisation->org_id}/roles/999"); + ])->getJson("/api/v1/organisation/{$organisation->org_id}/roles/00000000-0000-0000-0000-000000000000"); // Assert not found response $response->assertStatus(404) diff --git a/tests/Feature/ProductSearchTest.php b/tests/Feature/ProductSearchTest.php index 1374b03d..adba8b8b 100644 --- a/tests/Feature/ProductSearchTest.php +++ b/tests/Feature/ProductSearchTest.php @@ -52,42 +52,15 @@ public function it_returns_products_based_on_search_criteria() $category = $this->categories->first()->name; $product = $this->products->first(); - $response = $this->getJson("/api/v1/organisations/{$this->organisation->org_id}/products/search?name={$product->name}&category={$category}&minPrice=0&maxPrice=1000"); - + // $response = $this->getJson("/api/v1/organisations/{$this->organisation->org_id}/products/search?product_name={$product->name}&category={$category}&minPrice=0&maxPrice=1000"); + $response = $this->getJson("/api/v1/organisations/{$this->organisation->org_id}/products/search?product_name={$product->name}"); $response->assertStatus(200); - $response->assertJsonStructure([ - 'status_code', - 'message', - 'data' => [ - '*' => [ - 'id', - 'name', - 'price', - 'cost_price', - 'image', - 'description', - 'quantity', - 'category', - 'status', - 'size', - 'created_at', - 'updated_at', - 'deletedAt' - ] - ] - ]); } /** @test */ public function it_returns_no_results_when_no_products_match() { - $response = $this->getJson("/api/v1/organisations/{$this->organisation->org_id}/products/search?name=NonexistentProduct"); - - $response->assertStatus(200); - $response->assertJson([ - 'status_code' => 200, - 'message' => 'Products retrieved successfully', - 'data' => [] - ]); + $response = $this->getJson("/api/v1/organisations/{$this->organisation->org_id}/products/search?product_name=NonexistentProduct"); + $response->assertStatus(404); } } diff --git a/tests/Feature/RoleCreationTest.php b/tests/Feature/RoleCreationTest.php index cb7e8679..816f5c1b 100755 --- a/tests/Feature/RoleCreationTest.php +++ b/tests/Feature/RoleCreationTest.php @@ -1,9 +1,9 @@ test_permission = Permission::create([ 'name' => 'test permission 1', + 'description' => 'Test permission description', ]); + // Authenticate the test user + $this->actingAs($this->test_user, 'api'); + + // Ensure test data exists in the database $this->assertDatabaseHas('users', ['name' => $this->test_user->name]); $this->assertDatabaseHas('organisations', ['name' => $this->test_org->name]); - $this->assertDatabaseHas('permissions', ['name' => 'test permission 1']); + $this->assertDatabaseHas('permissions', ['name' => $this->test_permission->name]); } public function test_role_creation_is_successful() { - // Authenticate the test user - $this->actingAs($this->test_user, 'api'); + // Send the role creation request $response = $this->postJson('/api/v1/organisations/' . $this->test_org->org_id . '/roles', [ - 'role_name' => 'Test role', - 'organisation_id' => $this->test_org->org_id, - 'permissions_id' => $this->test_permission->id, + 'name' => 'Test role', + 'description' => 'Test role description', + 'permissions' => [$this->test_permission->id], ]); + // Decode the response to get the role ID + $responseData = $response->json(); + $createdRoleId = $responseData['data']['id']; + + // Assert that the response status is 201 (Created) and check the JSON structure $response->assertStatus(201) - ->assertJsonStructure([ - 'status_code', - 'message' + ->assertJson([ + 'status_code' => 201, + 'data' => [ + 'id' => $createdRoleId, // Check that an ID is returned + 'name' => 'Test role', + 'description' => 'Test role description', + 'permissions' => [ + [ + 'id' => $this->test_permission->permission_id, + 'name' => $this->test_permission->name, + ], + ], + ], + 'error' => null, + 'message' => 'Role created successfully', ]); + // Assert that the role is in the database with the correct name and description $this->assertDatabaseHas('roles', [ 'name' => 'Test role', - ])->assertDatabaseCount('roles_permissions', 1); + 'description' => 'Test role description', + ]); + + // Assert that the role has been correctly associated with the permission + $this->assertDatabaseCount('roles_permissions', 1); } + } diff --git a/tests/Feature/RoleDeletionTest.php b/tests/Feature/RoleDeletionTest.php index bac9d78b..71cf8adb 100644 --- a/tests/Feature/RoleDeletionTest.php +++ b/tests/Feature/RoleDeletionTest.php @@ -72,7 +72,7 @@ public function test_cannot_delete_non_existent_role() $this->actingAs($this->admin_user, 'api'); // Attempt to delete a non-existent role - $response = $this->deleteJson('/api/v1/organisations/' . $this->test_org->org_id . '/roles/9999'); + $response = $this->deleteJson('/api/v1/organisations/' . $this->test_org->org_id . '/roles/00000000-0000-0000-0000-000000000000'); // Assert a 404 Not Found response $response->assertStatus(404); diff --git a/tests/Feature/ShowRoleTest.php b/tests/Feature/ShowRoleTest.php index 85d2d496..06aa2c6d 100644 --- a/tests/Feature/ShowRoleTest.php +++ b/tests/Feature/ShowRoleTest.php @@ -108,7 +108,7 @@ public function testFetchRoleWithInvalidRoleIdFormat() public function testFetchRoleForNonExistentOrganisation() { // Use a UUID that does not exist in the database - $response = $this->getJson('/api/v1/organisations/00000000-0000-0000-0000-000000000000/roles/1'); + $response = $this->getJson('/api/v1/organisations/00000000-0000-0000-0000-000000000000/roles/'. Str::uuid()); $response->assertStatus(404) ->assertJson([ @@ -128,13 +128,13 @@ public function testFetchNonExistentRole() $organisation = Organisation::factory()->create(['user_id' => $this->user->id]); // Use a valid UUID for organisation but one that does not exist as a role - $response = $this->getJson("/api/v1/organisations/{$organisation->org_id}/roles/00000000000000000000000000000000"); + $response = $this->getJson("/api/v1/organisations/{$organisation->org_id}/roles/00000000-0000-0000-0000-000000000000"); $response->assertStatus(404) ->assertJson([ 'status_code' => 404, 'error' => 'Not Found', - 'message' => 'The role with ID 00000000000000000000000000000000 does not exist', + 'message' => 'The role with ID 00000000-0000-0000-0000-000000000000 does not exist', ]); } } diff --git a/tests/Feature/UserDeletionTest.php b/tests/Feature/UserDeletionTest.php new file mode 100644 index 00000000..ea4deaee --- /dev/null +++ b/tests/Feature/UserDeletionTest.php @@ -0,0 +1,172 @@ + '9cd7a1b4-92e9-434e-8f1d-be37d64d2835', + 'name' => 'Admin User', + 'email' => 'admin@example.com', + 'password' => bcrypt('@Bulldozer01'), // Admin password + 'role' => 'admin', + 'is_verified' => 1, + ]); + + // Attempt to delete a non-existent user + $response = $this->actingAs($admin)->deleteJson('/api/v1/users/f1c7a1b4-92e9-434e-8f1d-be37d64d2859'); + $response->assertStatus(404) + ->assertJson([ + 'statusCode' => 404, + 'message' => 'User not found', + ]); + } + + /** + * Test unauthorized user attempting to delete another user. + * + * @return void + */ + public function test_unauthorized_user_deleting_another_user() + { + // Create dummy users + $user = User::create([ + 'id' => 'f1c7a1b4-92e9-434e-8f1d-be37d64d2837', + 'name' => 'Regular User', + 'email' => 'user@example.com', + 'password' => bcrypt('password'), + 'role' => 'user', + 'is_verified' => 1, + ]); + + $anotherUser = User::create([ + 'id' => 'f2c7a1b4-92e9-434e-8f1d-be37d64d2838', + 'name' => 'Another User', + 'email' => 'anotheruser@example.com', + 'password' => bcrypt('password'), + 'role' => 'user', + 'is_verified' => 1, + ]); + + // Attempt to delete another user as a regular user + $response = $this->actingAs($user)->deleteJson("/api/v1/users/{$anotherUser->id}"); + $response->assertStatus(403) + ->assertJson([ + 'statusCode' => 403, + 'message' => 'Unauthorized to delete this user', + ]); + } + + /** + * Test admin deleting a user. + * + * @return void + */ + public function test_admin_deleting_user() + { + // Create dummy users + $admin = User::create([ + 'id' => '9cd7a1b4-92e9-434e-8f1d-be37d64d2835', + 'name' => 'Admin User', + 'email' => 'admin@example.com', + 'password' => bcrypt('@Bulldozer01'), // Admin password + 'role' => 'admin', + 'is_verified' => 1, + ]); + + $user = User::create([ + 'id' => 'f1c7a1b4-92e9-434e-8f1d-be37d64d2837', + 'name' => 'Regular User', + 'email' => 'user@example.com', + 'password' => bcrypt('password'), + 'role' => 'user', + 'is_verified' => 1, + ]); + + // Delete a user as an admin + $response = $this->actingAs($admin)->deleteJson("/api/v1/users/{$user->id}"); + $response->assertStatus(200) + ->assertJson([ + 'statusCode' => 200, + 'message' => 'User deleted successfully', + ]); + $this->assertSoftDeleted('users', ['id' => $user->id]); + } + + /** + * Test super admin deleting a user. + * + * @return void + */ + public function test_super_admin_deleting_user() + { + // Create dummy users + $superAdmin = User::create([ + 'id' => 'e0dba1b4-92e9-434e-8f1d-be37d64d2836', + 'name' => 'Super Admin User', + 'email' => 'superadmin@example.com', + 'password' => bcrypt('@Bulldozer01'), + 'role' => 'superAdmin', + 'is_verified' => 1, + ]); + + $anotherUser = User::create([ + 'id' => 'f3c7a1b4-92e9-434e-8f1d-be37d64d2839', + 'name' => 'Another User 2', + 'email' => 'anotheruser2@example.com', + 'password' => bcrypt('password'), + 'role' => 'user', + 'is_verified' => 1, + ]); + + // Delete a user as a super admin + $response = $this->actingAs($superAdmin)->deleteJson("/api/v1/users/{$anotherUser->id}"); + $response->assertStatus(200) + ->assertJson([ + 'statusCode' => 200, + 'message' => 'User deleted successfully', + ]); + $this->assertSoftDeleted('users', ['id' => $anotherUser->id]); + } + + /** + * Test deleting self as super admin. + * + * @return void + */ + public function test_deleting_self_as_super_admin() + { + // Create a super admin user + $superAdmin = User::create([ + 'id' => 'e0dba1b4-92e9-434e-8f1d-be37d64d2836', + 'name' => 'Super Admin User', + 'email' => 'superadmin@example.com', + 'password' => bcrypt('@Bulldozer01'), + 'role' => 'superAdmin', + 'is_verified' => 1, + ]); + + // Delete self as a super admin + $response = $this->actingAs($superAdmin)->deleteJson("/api/v1/users/{$superAdmin->id}"); + $response->assertStatus(200) + ->assertJson([ + 'statusCode' => 200, + 'message' => 'User deleted successfully', + ]); + $this->assertSoftDeleted('users', ['id' => $superAdmin->id]); + } +}