feat: Update API endpoint usage in repositories to remove redundant path property
- Refactored multiple repository implementations across client and staff features to directly use endpoint objects without accessing the `path` property. - Introduced a new `FeatureGate` class for client-side feature gating based on user scopes, allowing for better access control to API endpoints. - Added `ApiEndpoint` class to represent API endpoints with their paths and required scopes for future feature gating.
This commit is contained in:
@@ -23,6 +23,7 @@ export 'src/entities/enums/staff_status.dart';
|
||||
export 'src/entities/enums/user_role.dart';
|
||||
|
||||
// Core
|
||||
export 'src/core/services/api_services/api_endpoint.dart';
|
||||
export 'src/core/services/api_services/api_response.dart';
|
||||
export 'src/core/services/api_services/base_api_service.dart';
|
||||
export 'src/core/services/api_services/base_core_service.dart';
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/// Represents an API endpoint with its path and required scopes for future
|
||||
/// feature gating.
|
||||
class ApiEndpoint {
|
||||
/// Creates an [ApiEndpoint] with the given [path] and optional
|
||||
/// [requiredScopes].
|
||||
const ApiEndpoint(this.path, {this.requiredScopes = const <String>[]});
|
||||
|
||||
/// The relative URL path (e.g. '/auth/client/sign-in').
|
||||
final String path;
|
||||
|
||||
/// Scopes required to access this endpoint. Empty means no gate.
|
||||
final List<String> requiredScopes;
|
||||
|
||||
@override
|
||||
String toString() => path;
|
||||
}
|
||||
@@ -1,36 +1,38 @@
|
||||
import 'api_endpoint.dart';
|
||||
import 'api_response.dart';
|
||||
|
||||
/// Abstract base class for API services.
|
||||
///
|
||||
/// This defines the contract for making HTTP requests.
|
||||
/// Methods accept [ApiEndpoint] which carries the path and required scopes.
|
||||
/// Implementations should validate scopes via [FeatureGate] before executing.
|
||||
abstract class BaseApiService {
|
||||
/// Performs a GET request to the specified [endpoint].
|
||||
Future<ApiResponse> get(String endpoint, {Map<String, dynamic>? params});
|
||||
Future<ApiResponse> get(ApiEndpoint endpoint, {Map<String, dynamic>? params});
|
||||
|
||||
/// Performs a POST request to the specified [endpoint].
|
||||
Future<ApiResponse> post(
|
||||
String endpoint, {
|
||||
ApiEndpoint endpoint, {
|
||||
dynamic data,
|
||||
Map<String, dynamic>? params,
|
||||
});
|
||||
|
||||
/// Performs a PUT request to the specified [endpoint].
|
||||
Future<ApiResponse> put(
|
||||
String endpoint, {
|
||||
ApiEndpoint endpoint, {
|
||||
dynamic data,
|
||||
Map<String, dynamic>? params,
|
||||
});
|
||||
|
||||
/// Performs a PATCH request to the specified [endpoint].
|
||||
Future<ApiResponse> patch(
|
||||
String endpoint, {
|
||||
ApiEndpoint endpoint, {
|
||||
dynamic data,
|
||||
Map<String, dynamic>? params,
|
||||
});
|
||||
|
||||
/// Performs a DELETE request to the specified [endpoint].
|
||||
Future<ApiResponse> delete(
|
||||
String endpoint, {
|
||||
ApiEndpoint endpoint, {
|
||||
dynamic data,
|
||||
Map<String, dynamic>? params,
|
||||
});
|
||||
|
||||
@@ -330,3 +330,22 @@ class NotAuthenticatedException extends AppException {
|
||||
@override
|
||||
String get messageKey => 'errors.auth.not_authenticated';
|
||||
}
|
||||
|
||||
/// Thrown when the user lacks the required scopes to access an endpoint.
|
||||
class InsufficientScopeException extends AppException {
|
||||
/// Creates an [InsufficientScopeException].
|
||||
const InsufficientScopeException({
|
||||
required this.requiredScopes,
|
||||
required this.userScopes,
|
||||
super.technicalMessage,
|
||||
}) : super(code: 'SCOPE_001');
|
||||
|
||||
/// The scopes required by the endpoint.
|
||||
final List<String> requiredScopes;
|
||||
|
||||
/// The scopes the user currently has.
|
||||
final List<String> userScopes;
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.generic.insufficient_scope';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user