Compare commits
No commits in common. "feat/standardise-error-responses" and "dev" have entirely different histories.
feat/stand
...
dev
5 changed files with 90 additions and 170 deletions
|
|
@ -1,92 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Core\Tenant\Concerns;
|
|
||||||
|
|
||||||
use Illuminate\Http\JsonResponse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Standardised API response helpers for php-tenant controllers.
|
|
||||||
*
|
|
||||||
* All error responses follow the format:
|
|
||||||
* ```json
|
|
||||||
* {
|
|
||||||
* "success": false,
|
|
||||||
* "error": "Human-readable message",
|
|
||||||
* "code": "MACHINE_READABLE_CODE"
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* All success responses include `"success": true`.
|
|
||||||
*
|
|
||||||
* @see https://forge.lthn.ai/core/php-tenant/issues/20
|
|
||||||
*/
|
|
||||||
trait HasStandardApiResponses
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Return a standardised error response.
|
|
||||||
*/
|
|
||||||
protected function errorResponse(string $message, string $code, int $status = 400, array $extra = []): JsonResponse
|
|
||||||
{
|
|
||||||
return response()->json(array_merge([
|
|
||||||
'success' => false,
|
|
||||||
'error' => $message,
|
|
||||||
'code' => $code,
|
|
||||||
], $extra), $status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a standardised success response.
|
|
||||||
*/
|
|
||||||
protected function successResponse(array $data = [], int $status = 200): JsonResponse
|
|
||||||
{
|
|
||||||
return response()->json(array_merge([
|
|
||||||
'success' => true,
|
|
||||||
], $data), $status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a not found error response.
|
|
||||||
*/
|
|
||||||
protected function notFoundResponse(string $resource = 'Resource'): JsonResponse
|
|
||||||
{
|
|
||||||
return $this->errorResponse("{$resource} not found", 'NOT_FOUND', 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an unauthenticated error response.
|
|
||||||
*/
|
|
||||||
protected function unauthenticatedResponse(): JsonResponse
|
|
||||||
{
|
|
||||||
return $this->errorResponse('Unauthenticated', 'UNAUTHENTICATED', 401);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an access denied error response.
|
|
||||||
*/
|
|
||||||
protected function accessDeniedResponse(string $message = 'Access denied.'): JsonResponse
|
|
||||||
{
|
|
||||||
return $this->errorResponse($message, 'ACCESS_DENIED', 403);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a no workspace error response.
|
|
||||||
*/
|
|
||||||
protected function noWorkspaceResponse(): JsonResponse
|
|
||||||
{
|
|
||||||
return $this->errorResponse(
|
|
||||||
'No workspace found. Please select a workspace first.',
|
|
||||||
'NO_WORKSPACE',
|
|
||||||
404
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a validation / unprocessable entity error response.
|
|
||||||
*/
|
|
||||||
protected function unprocessableResponse(string $message, string $code = 'UNPROCESSABLE_ENTITY', array $extra = []): JsonResponse
|
|
||||||
{
|
|
||||||
return $this->errorResponse($message, $code, 422, $extra);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||||
namespace Core\Tenant\Controllers\Api;
|
namespace Core\Tenant\Controllers\Api;
|
||||||
|
|
||||||
use Core\Rules\SafeWebhookUrl;
|
use Core\Rules\SafeWebhookUrl;
|
||||||
use Core\Tenant\Concerns\HasStandardApiResponses;
|
|
||||||
use Core\Tenant\Exceptions\InvalidWebhookUrlException;
|
use Core\Tenant\Exceptions\InvalidWebhookUrlException;
|
||||||
use Core\Tenant\Models\EntitlementWebhook;
|
use Core\Tenant\Models\EntitlementWebhook;
|
||||||
use Core\Tenant\Models\EntitlementWebhookDelivery;
|
use Core\Tenant\Models\EntitlementWebhookDelivery;
|
||||||
|
|
@ -26,8 +25,6 @@ use Illuminate\Validation\Rule;
|
||||||
*/
|
*/
|
||||||
class EntitlementWebhookController extends Controller
|
class EntitlementWebhookController extends Controller
|
||||||
{
|
{
|
||||||
use HasStandardApiResponses;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected EntitlementWebhookService $webhookService
|
protected EntitlementWebhookService $webhookService
|
||||||
) {}
|
) {}
|
||||||
|
|
@ -74,16 +71,16 @@ class EntitlementWebhookController extends Controller
|
||||||
metadata: $validated['metadata'] ?? []
|
metadata: $validated['metadata'] ?? []
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->successResponse([
|
return response()->json([
|
||||||
'message' => __('Webhook created successfully'),
|
'message' => __('Webhook created successfully'),
|
||||||
'webhook' => $webhook,
|
'webhook' => $webhook,
|
||||||
'secret' => $webhook->secret, // Return secret on creation only
|
'secret' => $webhook->secret, // Return secret on creation only
|
||||||
], 201);
|
], 201);
|
||||||
} catch (InvalidWebhookUrlException $e) {
|
} catch (InvalidWebhookUrlException $e) {
|
||||||
return $this->unprocessableResponse(
|
return response()->json([
|
||||||
$e->getMessage(),
|
'message' => $e->getMessage(),
|
||||||
'INVALID_WEBHOOK_URL'
|
'error' => 'invalid_webhook_url',
|
||||||
);
|
], 422);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,7 +94,7 @@ class EntitlementWebhookController extends Controller
|
||||||
$webhook->loadCount('deliveries');
|
$webhook->loadCount('deliveries');
|
||||||
$webhook->load(['deliveries' => fn ($q) => $q->latest('created_at')->limit(10)]);
|
$webhook->load(['deliveries' => fn ($q) => $q->latest('created_at')->limit(10)]);
|
||||||
|
|
||||||
return $this->successResponse([
|
return response()->json([
|
||||||
'webhook' => $webhook,
|
'webhook' => $webhook,
|
||||||
'available_events' => $this->webhookService->getAvailableEvents(),
|
'available_events' => $this->webhookService->getAvailableEvents(),
|
||||||
]);
|
]);
|
||||||
|
|
@ -123,15 +120,15 @@ class EntitlementWebhookController extends Controller
|
||||||
try {
|
try {
|
||||||
$webhook = $this->webhookService->update($webhook, $validated);
|
$webhook = $this->webhookService->update($webhook, $validated);
|
||||||
|
|
||||||
return $this->successResponse([
|
return response()->json([
|
||||||
'message' => __('Webhook updated successfully'),
|
'message' => __('Webhook updated successfully'),
|
||||||
'webhook' => $webhook,
|
'webhook' => $webhook,
|
||||||
]);
|
]);
|
||||||
} catch (InvalidWebhookUrlException $e) {
|
} catch (InvalidWebhookUrlException $e) {
|
||||||
return $this->unprocessableResponse(
|
return response()->json([
|
||||||
$e->getMessage(),
|
'message' => $e->getMessage(),
|
||||||
'INVALID_WEBHOOK_URL'
|
'error' => 'invalid_webhook_url',
|
||||||
);
|
], 422);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,7 +141,7 @@ class EntitlementWebhookController extends Controller
|
||||||
|
|
||||||
$this->webhookService->unregister($webhook);
|
$this->webhookService->unregister($webhook);
|
||||||
|
|
||||||
return $this->successResponse([
|
return response()->json([
|
||||||
'message' => __('Webhook deleted successfully'),
|
'message' => __('Webhook deleted successfully'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
@ -158,7 +155,7 @@ class EntitlementWebhookController extends Controller
|
||||||
|
|
||||||
$secret = $webhook->regenerateSecret();
|
$secret = $webhook->regenerateSecret();
|
||||||
|
|
||||||
return $this->successResponse([
|
return response()->json([
|
||||||
'message' => __('Secret regenerated successfully'),
|
'message' => __('Secret regenerated successfully'),
|
||||||
'secret' => $secret,
|
'secret' => $secret,
|
||||||
]);
|
]);
|
||||||
|
|
@ -177,17 +174,18 @@ class EntitlementWebhookController extends Controller
|
||||||
try {
|
try {
|
||||||
$delivery = $this->webhookService->testWebhook($webhook);
|
$delivery = $this->webhookService->testWebhook($webhook);
|
||||||
|
|
||||||
return $this->successResponse([
|
return response()->json([
|
||||||
'message' => $delivery->isSucceeded()
|
'message' => $delivery->isSucceeded()
|
||||||
? __('Test webhook sent successfully')
|
? __('Test webhook sent successfully')
|
||||||
: __('Test webhook failed'),
|
: __('Test webhook failed'),
|
||||||
'delivery' => $delivery,
|
'delivery' => $delivery,
|
||||||
]);
|
]);
|
||||||
} catch (InvalidWebhookUrlException $e) {
|
} catch (InvalidWebhookUrlException $e) {
|
||||||
return $this->unprocessableResponse(
|
return response()->json([
|
||||||
$e->getMessage(),
|
'message' => $e->getMessage(),
|
||||||
'INVALID_WEBHOOK_URL'
|
'error' => 'invalid_webhook_url',
|
||||||
);
|
'reason' => $e->reason,
|
||||||
|
], 422);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,7 +198,7 @@ class EntitlementWebhookController extends Controller
|
||||||
|
|
||||||
$this->webhookService->resetCircuitBreaker($webhook);
|
$this->webhookService->resetCircuitBreaker($webhook);
|
||||||
|
|
||||||
return $this->successResponse([
|
return response()->json([
|
||||||
'message' => __('Webhook re-enabled successfully'),
|
'message' => __('Webhook re-enabled successfully'),
|
||||||
'webhook' => $webhook->refresh(),
|
'webhook' => $webhook->refresh(),
|
||||||
]);
|
]);
|
||||||
|
|
@ -228,15 +226,14 @@ class EntitlementWebhookController extends Controller
|
||||||
$this->authorizeWebhook($request, $delivery->webhook);
|
$this->authorizeWebhook($request, $delivery->webhook);
|
||||||
|
|
||||||
if ($delivery->isSucceeded()) {
|
if ($delivery->isSucceeded()) {
|
||||||
return $this->unprocessableResponse(
|
return response()->json([
|
||||||
__('Cannot retry a successful delivery'),
|
'message' => __('Cannot retry a successful delivery'),
|
||||||
'DELIVERY_ALREADY_SUCCEEDED'
|
], 422);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$delivery = $this->webhookService->retryDelivery($delivery);
|
$delivery = $this->webhookService->retryDelivery($delivery);
|
||||||
|
|
||||||
return $this->successResponse([
|
return response()->json([
|
||||||
'message' => $delivery->isSucceeded()
|
'message' => $delivery->isSucceeded()
|
||||||
? __('Delivery retried successfully')
|
? __('Delivery retried successfully')
|
||||||
: __('Delivery retry failed'),
|
: __('Delivery retry failed'),
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ namespace Core\Tenant\Controllers;
|
||||||
|
|
||||||
use Core\Api\RateLimit\RateLimit;
|
use Core\Api\RateLimit\RateLimit;
|
||||||
use Core\Front\Controller;
|
use Core\Front\Controller;
|
||||||
use Core\Tenant\Concerns\HasStandardApiResponses;
|
|
||||||
use Core\Tenant\Models\EntitlementLog;
|
use Core\Tenant\Models\EntitlementLog;
|
||||||
use Core\Tenant\Models\Package;
|
use Core\Tenant\Models\Package;
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
|
|
@ -44,8 +43,6 @@ use Illuminate\Support\Str;
|
||||||
#[RateLimit(limit: 60, window: 60, key: 'entitlement-api')]
|
#[RateLimit(limit: 60, window: 60, key: 'entitlement-api')]
|
||||||
class EntitlementApiController extends Controller
|
class EntitlementApiController extends Controller
|
||||||
{
|
{
|
||||||
use HasStandardApiResponses;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected EntitlementService $entitlements
|
protected EntitlementService $entitlements
|
||||||
) {}
|
) {}
|
||||||
|
|
@ -92,11 +89,10 @@ class EntitlementApiController extends Controller
|
||||||
$package = Package::where('code', $validated['product_code'])->first();
|
$package = Package::where('code', $validated['product_code'])->first();
|
||||||
|
|
||||||
if (! $package) {
|
if (! $package) {
|
||||||
return $this->errorResponse(
|
return response()->json([
|
||||||
"Package '{$validated['product_code']}' not found",
|
'success' => false,
|
||||||
'PACKAGE_NOT_FOUND',
|
'error' => "Package '{$validated['product_code']}' not found",
|
||||||
404
|
], 404);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get or create the user's primary workspace
|
// Get or create the user's primary workspace
|
||||||
|
|
@ -137,7 +133,8 @@ class EntitlementApiController extends Controller
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->successResponse([
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
'entitlement_id' => $workspacePackage->id,
|
'entitlement_id' => $workspacePackage->id,
|
||||||
'workspace_id' => $workspace->id,
|
'workspace_id' => $workspace->id,
|
||||||
'workspace_slug' => $workspace->slug,
|
'workspace_slug' => $workspace->slug,
|
||||||
|
|
@ -154,7 +151,10 @@ class EntitlementApiController extends Controller
|
||||||
$workspacePackage = WorkspacePackage::find($id);
|
$workspacePackage = WorkspacePackage::find($id);
|
||||||
|
|
||||||
if (! $workspacePackage) {
|
if (! $workspacePackage) {
|
||||||
return $this->notFoundResponse('Entitlement');
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Entitlement not found',
|
||||||
|
], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
$workspace = $workspacePackage->workspace;
|
$workspace = $workspacePackage->workspace;
|
||||||
|
|
@ -170,7 +170,8 @@ class EntitlementApiController extends Controller
|
||||||
|
|
||||||
$this->entitlements->invalidateCache($workspace);
|
$this->entitlements->invalidateCache($workspace);
|
||||||
|
|
||||||
return $this->successResponse([
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
'entitlement_id' => $workspacePackage->id,
|
'entitlement_id' => $workspacePackage->id,
|
||||||
'status' => $workspacePackage->fresh()->status,
|
'status' => $workspacePackage->fresh()->status,
|
||||||
]);
|
]);
|
||||||
|
|
@ -184,7 +185,10 @@ class EntitlementApiController extends Controller
|
||||||
$workspacePackage = WorkspacePackage::find($id);
|
$workspacePackage = WorkspacePackage::find($id);
|
||||||
|
|
||||||
if (! $workspacePackage) {
|
if (! $workspacePackage) {
|
||||||
return $this->notFoundResponse('Entitlement');
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Entitlement not found',
|
||||||
|
], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
$workspace = $workspacePackage->workspace;
|
$workspace = $workspacePackage->workspace;
|
||||||
|
|
@ -199,7 +203,8 @@ class EntitlementApiController extends Controller
|
||||||
|
|
||||||
$this->entitlements->invalidateCache($workspace);
|
$this->entitlements->invalidateCache($workspace);
|
||||||
|
|
||||||
return $this->successResponse([
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
'entitlement_id' => $workspacePackage->id,
|
'entitlement_id' => $workspacePackage->id,
|
||||||
'status' => $workspacePackage->fresh()->status,
|
'status' => $workspacePackage->fresh()->status,
|
||||||
]);
|
]);
|
||||||
|
|
@ -213,7 +218,10 @@ class EntitlementApiController extends Controller
|
||||||
$workspacePackage = WorkspacePackage::find($id);
|
$workspacePackage = WorkspacePackage::find($id);
|
||||||
|
|
||||||
if (! $workspacePackage) {
|
if (! $workspacePackage) {
|
||||||
return $this->notFoundResponse('Entitlement');
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Entitlement not found',
|
||||||
|
], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
$workspace = $workspacePackage->workspace;
|
$workspace = $workspacePackage->workspace;
|
||||||
|
|
@ -229,7 +237,8 @@ class EntitlementApiController extends Controller
|
||||||
|
|
||||||
$this->entitlements->invalidateCache($workspace);
|
$this->entitlements->invalidateCache($workspace);
|
||||||
|
|
||||||
return $this->successResponse([
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
'entitlement_id' => $workspacePackage->id,
|
'entitlement_id' => $workspacePackage->id,
|
||||||
'status' => $workspacePackage->fresh()->status,
|
'status' => $workspacePackage->fresh()->status,
|
||||||
]);
|
]);
|
||||||
|
|
@ -248,7 +257,10 @@ class EntitlementApiController extends Controller
|
||||||
$workspacePackage = WorkspacePackage::find($id);
|
$workspacePackage = WorkspacePackage::find($id);
|
||||||
|
|
||||||
if (! $workspacePackage) {
|
if (! $workspacePackage) {
|
||||||
return $this->notFoundResponse('Entitlement');
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Entitlement not found',
|
||||||
|
], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
$workspace = $workspacePackage->workspace;
|
$workspace = $workspacePackage->workspace;
|
||||||
|
|
@ -279,7 +291,8 @@ class EntitlementApiController extends Controller
|
||||||
|
|
||||||
$this->entitlements->invalidateCache($workspace);
|
$this->entitlements->invalidateCache($workspace);
|
||||||
|
|
||||||
return $this->successResponse([
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
'entitlement_id' => $workspacePackage->id,
|
'entitlement_id' => $workspacePackage->id,
|
||||||
'status' => $workspacePackage->fresh()->status,
|
'status' => $workspacePackage->fresh()->status,
|
||||||
'expires_at' => $workspacePackage->fresh()->expires_at?->toIso8601String(),
|
'expires_at' => $workspacePackage->fresh()->expires_at?->toIso8601String(),
|
||||||
|
|
@ -294,10 +307,14 @@ class EntitlementApiController extends Controller
|
||||||
$workspacePackage = WorkspacePackage::with(['package', 'workspace'])->find($id);
|
$workspacePackage = WorkspacePackage::with(['package', 'workspace'])->find($id);
|
||||||
|
|
||||||
if (! $workspacePackage) {
|
if (! $workspacePackage) {
|
||||||
return $this->notFoundResponse('Entitlement');
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Entitlement not found',
|
||||||
|
], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->successResponse([
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
'entitlement' => [
|
'entitlement' => [
|
||||||
'id' => $workspacePackage->id,
|
'id' => $workspacePackage->id,
|
||||||
'workspace_id' => $workspacePackage->workspace_id,
|
'workspace_id' => $workspacePackage->workspace_id,
|
||||||
|
|
@ -339,20 +356,22 @@ class EntitlementApiController extends Controller
|
||||||
$user = User::where('email', $validated['email'])->first();
|
$user = User::where('email', $validated['email'])->first();
|
||||||
|
|
||||||
if (! $user) {
|
if (! $user) {
|
||||||
return $this->errorResponse('User not found', 'USER_NOT_FOUND', 404, [
|
return response()->json([
|
||||||
'allowed' => false,
|
'allowed' => false,
|
||||||
|
'reason' => 'User not found',
|
||||||
'feature_code' => $validated['feature'],
|
'feature_code' => $validated['feature'],
|
||||||
]);
|
], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user's primary workspace
|
// Get user's primary workspace
|
||||||
$workspace = $user->defaultHostWorkspace();
|
$workspace = $user->defaultHostWorkspace();
|
||||||
|
|
||||||
if (! $workspace) {
|
if (! $workspace) {
|
||||||
return $this->errorResponse('No workspace found for user', 'NO_WORKSPACE', 404, [
|
return response()->json([
|
||||||
'allowed' => false,
|
'allowed' => false,
|
||||||
|
'reason' => 'No workspace found for user',
|
||||||
'feature_code' => $validated['feature'],
|
'feature_code' => $validated['feature'],
|
||||||
]);
|
], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check entitlement
|
// Check entitlement
|
||||||
|
|
@ -392,14 +411,20 @@ class EntitlementApiController extends Controller
|
||||||
$user = User::where('email', $validated['email'])->first();
|
$user = User::where('email', $validated['email'])->first();
|
||||||
|
|
||||||
if (! $user) {
|
if (! $user) {
|
||||||
return $this->errorResponse('User not found', 'USER_NOT_FOUND', 404);
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'User not found',
|
||||||
|
], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user's primary workspace
|
// Get user's primary workspace
|
||||||
$workspace = $user->defaultHostWorkspace();
|
$workspace = $user->defaultHostWorkspace();
|
||||||
|
|
||||||
if (! $workspace) {
|
if (! $workspace) {
|
||||||
return $this->errorResponse('No workspace found for user', 'NO_WORKSPACE', 404);
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'No workspace found for user',
|
||||||
|
], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record usage
|
// Record usage
|
||||||
|
|
@ -411,7 +436,8 @@ class EntitlementApiController extends Controller
|
||||||
$validated['metadata'] ?? null
|
$validated['metadata'] ?? null
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->successResponse([
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
'usage_record_id' => $record->id,
|
'usage_record_id' => $record->id,
|
||||||
'feature_code' => $validated['feature'],
|
'feature_code' => $validated['feature'],
|
||||||
'quantity' => $validated['quantity'] ?? 1,
|
'quantity' => $validated['quantity'] ?? 1,
|
||||||
|
|
@ -474,13 +500,17 @@ class EntitlementApiController extends Controller
|
||||||
$user = $request->user();
|
$user = $request->user();
|
||||||
|
|
||||||
if (! $user) {
|
if (! $user) {
|
||||||
return $this->unauthenticatedResponse();
|
return response()->json([
|
||||||
|
'error' => 'Unauthenticated',
|
||||||
|
], 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
$workspace = $user->defaultHostWorkspace();
|
$workspace = $user->defaultHostWorkspace();
|
||||||
|
|
||||||
if (! $workspace) {
|
if (! $workspace) {
|
||||||
return $this->errorResponse('No workspace found', 'NO_WORKSPACE', 404);
|
return response()->json([
|
||||||
|
'error' => 'No workspace found',
|
||||||
|
], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->summary($request, $workspace);
|
return $this->summary($request, $workspace);
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,13 @@ declare(strict_types=1);
|
||||||
namespace Core\Tenant\Controllers;
|
namespace Core\Tenant\Controllers;
|
||||||
|
|
||||||
use Core\Front\Controller;
|
use Core\Front\Controller;
|
||||||
use Core\Tenant\Concerns\HasStandardApiResponses;
|
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Mod\Api\Controllers\Concerns\HasApiResponses;
|
||||||
use Mod\Api\Controllers\Concerns\ResolvesWorkspace;
|
use Mod\Api\Controllers\Concerns\ResolvesWorkspace;
|
||||||
use Mod\Api\Resources\PaginatedCollection;
|
use Mod\Api\Resources\PaginatedCollection;
|
||||||
use Mod\Api\Resources\WorkspaceResource;
|
use Mod\Api\Resources\WorkspaceResource;
|
||||||
|
|
@ -24,7 +24,7 @@ use Mod\Api\Resources\WorkspaceResource;
|
||||||
*/
|
*/
|
||||||
class WorkspaceController extends Controller
|
class WorkspaceController extends Controller
|
||||||
{
|
{
|
||||||
use HasStandardApiResponses;
|
use HasApiResponses;
|
||||||
use ResolvesWorkspace;
|
use ResolvesWorkspace;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -219,10 +219,10 @@ class WorkspaceController extends Controller
|
||||||
// Prevent deleting user's only workspace
|
// Prevent deleting user's only workspace
|
||||||
$workspaceCount = $user->workspaces()->count();
|
$workspaceCount = $user->workspaces()->count();
|
||||||
if ($workspaceCount <= 1) {
|
if ($workspaceCount <= 1) {
|
||||||
return $this->unprocessableResponse(
|
return response()->json([
|
||||||
'You cannot delete your only workspace.',
|
'error' => 'cannot_delete',
|
||||||
'CANNOT_DELETE_LAST_WORKSPACE'
|
'message' => 'You cannot delete your only workspace.',
|
||||||
);
|
], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
$workspace->delete();
|
$workspace->delete();
|
||||||
|
|
|
||||||
|
|
@ -118,10 +118,8 @@ describe('Cross-App Entitlement API', function () {
|
||||||
|
|
||||||
$response->assertStatus(404)
|
$response->assertStatus(404)
|
||||||
->assertJson([
|
->assertJson([
|
||||||
'success' => false,
|
|
||||||
'error' => 'User not found',
|
|
||||||
'code' => 'USER_NOT_FOUND',
|
|
||||||
'allowed' => false,
|
'allowed' => false,
|
||||||
|
'reason' => 'User not found',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -133,10 +131,8 @@ describe('Cross-App Entitlement API', function () {
|
||||||
|
|
||||||
$response->assertStatus(404)
|
$response->assertStatus(404)
|
||||||
->assertJson([
|
->assertJson([
|
||||||
'success' => false,
|
|
||||||
'error' => 'No workspace found for user',
|
|
||||||
'code' => 'NO_WORKSPACE',
|
|
||||||
'allowed' => false,
|
'allowed' => false,
|
||||||
|
'reason' => 'No workspace found for user',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -270,7 +266,6 @@ describe('Cross-App Entitlement API', function () {
|
||||||
->assertJson([
|
->assertJson([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'error' => 'User not found',
|
'error' => 'User not found',
|
||||||
'code' => 'USER_NOT_FOUND',
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -287,7 +282,6 @@ describe('Cross-App Entitlement API', function () {
|
||||||
->assertJson([
|
->assertJson([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'error' => 'No workspace found for user',
|
'error' => 'No workspace found for user',
|
||||||
'code' => 'NO_WORKSPACE',
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -362,9 +356,7 @@ describe('Cross-App Entitlement API', function () {
|
||||||
|
|
||||||
$response->assertStatus(404)
|
$response->assertStatus(404)
|
||||||
->assertJson([
|
->assertJson([
|
||||||
'success' => false,
|
|
||||||
'error' => 'No workspace found',
|
'error' => 'No workspace found',
|
||||||
'code' => 'NO_WORKSPACE',
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -514,7 +506,6 @@ describe('Blesta Provisioning API', function () {
|
||||||
->assertJson([
|
->assertJson([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'error' => "Package 'nonexistent-package' not found",
|
'error' => "Package 'nonexistent-package' not found",
|
||||||
'code' => 'PACKAGE_NOT_FOUND',
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -643,7 +634,6 @@ describe('Blesta Provisioning API', function () {
|
||||||
->assertJson([
|
->assertJson([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'error' => 'Entitlement not found',
|
'error' => 'Entitlement not found',
|
||||||
'code' => 'NOT_FOUND',
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -708,7 +698,6 @@ describe('Blesta Provisioning API', function () {
|
||||||
->assertJson([
|
->assertJson([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'error' => 'Entitlement not found',
|
'error' => 'Entitlement not found',
|
||||||
'code' => 'NOT_FOUND',
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -793,7 +782,6 @@ describe('Blesta Provisioning API', function () {
|
||||||
->assertJson([
|
->assertJson([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'error' => 'Entitlement not found',
|
'error' => 'Entitlement not found',
|
||||||
'code' => 'NOT_FOUND',
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -864,7 +852,6 @@ describe('Blesta Provisioning API', function () {
|
||||||
->assertJson([
|
->assertJson([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'error' => 'Entitlement not found',
|
'error' => 'Entitlement not found',
|
||||||
'code' => 'NOT_FOUND',
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -948,7 +935,6 @@ describe('Blesta Provisioning API', function () {
|
||||||
->assertJson([
|
->assertJson([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'error' => 'Entitlement not found',
|
'error' => 'Entitlement not found',
|
||||||
'code' => 'NOT_FOUND',
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1077,7 +1063,6 @@ describe('Error Response Format', function () {
|
||||||
->assertJsonStructure([
|
->assertJsonStructure([
|
||||||
'success',
|
'success',
|
||||||
'error',
|
'error',
|
||||||
'code',
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue