add: adding ported files from symfony httpfoundation component

This commit is contained in:
Patrick Stewart 2024-07-05 01:41:43 -07:00
parent 44981b881e
commit 7b0a8738ce
8 changed files with 589 additions and 4 deletions

View file

@ -0,0 +1,27 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// This library exports various foundation classes and utilities for web-related operations.
///
/// It includes:
/// - [CountableInterface] for objects that can be counted
/// - [Cookie] for handling HTTP cookies
/// - [HeaderUtils] for working with HTTP headers
/// - [HeaderBag] for managing collections of HTTP headers
/// - [ParameterBag] for handling request parameters
/// - [ResponseHeaderBag] for managing response headers
library;
export 'src/foundation/countable_interface.dart';
export 'src/foundation/stringable_interface.dart';
export 'src/foundation/cookie.dart';
export 'src/foundation/header_utils.dart';
export 'src/foundation/header_bag.dart';
export 'src/foundation/parameter_bag.dart';
export 'src/foundation/response_header_bag.dart';

View file

@ -0,0 +1,23 @@
/*
* This file is part of the Protevus Platform.
*
* (C) Protevus <developers@protevus.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// This library exports various exception classes and interfaces.
///
/// The exported classes include:
/// - `BadRequestException`: Used for handling bad HTTP requests.
/// - `RequestExceptionInterface`: An interface for request-related exceptions.
/// - `UnexpectedValueException`: Used when an unexpected value is encountered.
///
/// These exports allow other parts of the application to use these
/// exception classes and interfaces without needing to import them directly.
library;
export 'src/foundation/exception/bad_request_exception.dart';
export 'src/foundation/exception/request_exception_interface.dart';
export 'src/foundation/exception/unexpected_value_exception.dart';

View file

@ -10,7 +10,7 @@
*/
import 'dart:math';
import 'header_utils.dart';
import 'package:protevus_http/foundation.dart';
/// Represents an HTTP cookie.
///

View file

@ -0,0 +1,51 @@
/*
* This file is part of the Protevus Platform.
* This file is a port of the symfony BadRequestException.php class to Dart
*
* (C) Protevus <developers@protevus.com>
* (C) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_http/foundation_exception.dart';
/// Exception thrown when a user sends a malformed request.
///
/// This exception is used to indicate that the client's request was invalid or
/// could not be served. It extends [UnexpectedValueException] and implements
/// [RequestExceptionInterface].
///
/// Example usage:
/// ```dart
/// throw BadRequestException('Invalid parameter: id must be a positive integer');
/// ```
class BadRequestException extends UnexpectedValueException implements RequestExceptionInterface {
/// Creates a new [BadRequestException] with an optional error message.
///
/// The [message] parameter is passed to the superclass constructor.
/// If not provided, the exception will be created with an empty message.
///
/// Example:
/// ```dart
/// throw BadRequestException('Invalid input');
/// ```
BadRequestException([super.message]);
/// Returns a string representation of the [BadRequestException].
///
/// If the exception message is empty, it returns 'BadRequestException'.
/// Otherwise, it returns 'BadRequestException: ' followed by the exception message.
///
/// Example:
/// ```dart
/// var exception = BadRequestException('Invalid input');
/// print(exception.toString()); // Output: BadRequestException: Invalid input
/// ```
@override
String toString() {
return message.isEmpty ? 'BadRequestException' : 'BadRequestException: $message';
}
}

View file

@ -0,0 +1,22 @@
/*
* This file is part of the Protevus Platform.
* This file is a port of the symfony RequestExceptionInterface.php class to Dart
*
* (C) Protevus <developers@protevus.com>
* (C) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// An interface for exceptions related to HTTP requests.
///
/// Implementations of this interface are intended to be used for exceptions
/// that should trigger an HTTP 400 (Bad Request) response in the application.
///
/// This interface doesn't declare any methods, but serves as a marker
/// to identify exceptions specifically related to request handling.
abstract class RequestExceptionInterface {
}

View file

@ -0,0 +1,44 @@
/*
* This file is part of the Protevus Platform.
* This file is a port of the symfony UnexpectedValueException.php class to Dart
*
* (C) Protevus <developers@protevus.com>
* (C) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'dart:core';
/// Exception thrown if a value does not match with a set of values.
///
/// Typically this happens when a function calls another function and expects
/// the return value to be of a certain type or value not including arithmetic
/// or buffer related errors.
class UnexpectedValueException implements Exception {
/// The error message associated with this exception.
///
/// This is a final String that stores the descriptive message for the
/// UnexpectedValueException. It provides details about why the exception
/// was thrown and can be used for logging or displaying error information.
final String message;
/// Constructor for UnexpectedValueException.
///
/// Creates a new instance of UnexpectedValueException with an optional error message.
///
/// @param message The error message for this exception. Defaults to an empty string.
UnexpectedValueException([this.message = '']);
/// Returns a string representation of the UnexpectedValueException.
///
/// This method overrides the default toString() method to provide a more
/// descriptive string representation of the exception. If the exception
/// message is empty, it returns just the exception name. Otherwise, it
/// returns the exception name followed by a colon and the error message.
///
/// @return A string representation of the UnexpectedValueException.
@override
String toString() => message.isEmpty ? 'UnexpectedValueException' : 'UnexpectedValueException: $message';
}

View file

@ -0,0 +1,420 @@
/*
* This file is part of the Protevus Platform.
* This file is a port of the symfony ParameterBag.php class to Dart
*
* (C) Protevus <developers@protevus.com>
* (C) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'package:protevus_http/foundation.dart';
import 'package:protevus_http/foundation_exception.dart';
/// ParameterBag is an abstract container for key/value pairs.
/// It implements Iterable<MapEntry<String, dynamic>> and Countable interfaces.
abstract class ParameterBag implements Iterable<MapEntry<String, dynamic>>, Countable {
/// The underlying map containing the parameters.
///
/// This getter provides access to the internal map that stores all the key-value pairs
/// of parameters. The keys are of type [String], and the values can be of any type,
/// hence [dynamic].
///
/// This map is the core data structure of the ParameterBag, allowing for storage and
/// retrieval of various parameters used throughout the application.
Map<String, dynamic> get parameters;
/// Returns all parameters or a specific nested parameter.
///
/// If [key] is null, this method returns all parameters as a [Map<String, dynamic>].
/// If [key] is provided, it returns the value associated with that key, which must be
/// a [Map<String, dynamic>]. If the value is not a Map, it throws a [BadRequestException].
///
/// Parameters:
/// [key] - Optional. The key of the nested parameter to retrieve.
///
/// Returns:
/// A [Map<String, dynamic>] containing either all parameters or the nested parameter.
///
/// Throws:
/// [BadRequestException] if the value for the given key is not a Map<String, dynamic>.
Map<String, dynamic> all([String? key]) {
if (key == null) {
return parameters;
}
final value = parameters[key];
if (value is! Map<String, dynamic>) {
throw BadRequestException(
'Unexpected value for parameter "$key": expecting "Map", got "${value.runtimeType}".');
}
return value;
}
/// Returns a list of all parameter keys.
///
/// This method retrieves all the keys from the [parameters] map and returns them
/// as a [List<String>]. This can be useful when you need to iterate over or
/// inspect all the keys in the parameter bag without accessing their values.
///
/// Returns:
/// A [List<String>] containing all the keys from the parameters map.
List<String> keys() {
return parameters.keys.toList();
}
/// Replaces the current parameters with a new set of parameters.
///
/// This method completely replaces the existing parameters in the ParameterBag
/// with the new parameters provided in the [parameters] argument.
///
/// Parameters:
/// [parameters] - A Map<String, dynamic> containing the new set of parameters
/// that will replace the existing ones.
///
/// Example:
/// parameterBag.replace({'key1': 'value1', 'key2': 42});
void replace(Map<String, dynamic> parameters);
/// Adds new parameters to the existing set of parameters.
///
/// This method merges the provided [parameters] with the existing parameters
/// in the ParameterBag. If a key already exists, its value will be updated
/// with the new value from the provided map.
///
/// Parameters:
/// [parameters] - A Map<String, dynamic> containing the new parameters
/// to be added to the existing set.
///
/// Example:
/// parameterBag.add({'key1': 'newValue', 'key3': 'value3'});
void add(Map<String, dynamic> parameters);
/// Retrieves the value associated with the given key from the parameters.
///
/// This method searches for the specified [key] in the parameters map.
/// If the key is found, it returns the corresponding value.
/// If the key is not found, it returns the [defaultValue].
///
/// Parameters:
/// [key] - The key to look up in the parameters map.
/// [defaultValue] - Optional. The value to return if the key is not found.
/// If not provided, it defaults to null.
///
/// Returns:
/// The value associated with the key if found, otherwise the defaultValue.
///
/// Example:
/// var value = parameterBag.get('username', 'guest');
dynamic get(String key, [dynamic defaultValue]) {
return parameters.containsKey(key) ? parameters[key] : defaultValue;
}
/// Sets a parameter value for the given key.
///
/// This method adds or updates a parameter in the ParameterBag.
/// If the key already exists, its value will be updated.
/// If the key doesn't exist, a new key-value pair will be added.
///
/// Parameters:
/// [key] - The string key for the parameter.
/// [value] - The value to be associated with the key. Can be of any type.
///
/// Example:
/// parameterBag.set('username', 'john_doe');
/// parameterBag.set('age', 30);
void set(String key, dynamic value);
/// Checks if a parameter with the given key exists in the ParameterBag.
///
/// This method determines whether the specified [key] is present in the
/// parameters map. It returns true if the key exists, and false otherwise.
///
/// Parameters:
/// [key] - The string key to check for existence in the parameters map.
///
/// Returns:
/// A boolean value: true if the key exists, false otherwise.
///
/// Example:
/// if (parameterBag.has('username')) {
/// // Do something with the username parameter
/// }
bool has(String key) {
return parameters.containsKey(key);
}
/// Removes a parameter from the ParameterBag.
///
/// This method removes the key-value pair associated with the given [key]
/// from the parameters map. If the key doesn't exist, this method does nothing.
///
/// Parameters:
/// [key] - The string key of the parameter to be removed.
///
/// Example:
/// parameterBag.remove('username');
void remove(String key);
/// Returns the alphabetic characters of the parameter value.
///
/// This method retrieves the value associated with the given [key] as a string,
/// and then removes all non-alphabetic characters from it. If the key doesn't
/// exist or its value is empty, it returns the [defaultValue].
///
/// Parameters:
/// [key] - The key of the parameter to retrieve and process.
/// [defaultValue] - Optional. The value to return if the key is not found
/// or its value is empty. Defaults to an empty string.
///
/// Returns:
/// A string containing only alphabetic characters (a-z and A-Z) from the
/// original parameter value.
///
/// Example:
/// parameterBag.set('mixed', 'abc123XYZ!@#');
/// print(parameterBag.getAlpha('mixed')); // Outputs: 'abcXYZ'
String getAlpha(String key, [String defaultValue = '']) {
return getString(key, defaultValue).replaceAll(RegExp(r'[^a-zA-Z]'), '');
}
/// Returns the alphanumeric characters of the parameter value.
///
/// This method retrieves the value associated with the given [key] as a string,
/// and then removes all non-alphanumeric characters from it. If the key doesn't
/// exist or its value is empty, it returns the [defaultValue].
///
/// Parameters:
/// [key] - The key of the parameter to retrieve and process.
/// [defaultValue] - Optional. The value to return if the key is not found
/// or its value is empty. Defaults to an empty string.
///
/// Returns:
/// A string containing only alphanumeric characters (a-z, A-Z, and 0-9) from the
/// original parameter value.
///
/// Example:
/// parameterBag.set('mixed', 'abc123XYZ!@#');
/// print(parameterBag.getAlnum('mixed')); // Outputs: 'abc123XYZ'
String getAlnum(String key, [String defaultValue = '']) {
return getString(key, defaultValue).replaceAll(RegExp(r'[^a-zA-Z0-9]'), '');
}
/// Returns only the digit characters from the parameter value.
///
/// This method retrieves the value associated with the given [key] as a string,
/// and then removes all non-digit characters from it. If the key doesn't
/// exist or its value is empty, it returns the [defaultValue].
///
/// Parameters:
/// [key] - The key of the parameter to retrieve and process.
/// [defaultValue] - Optional. The value to return if the key is not found
/// or its value is empty. Defaults to an empty string.
///
/// Returns:
/// A string containing only digit characters (0-9) from the original parameter value.
///
/// Example:
/// parameterBag.set('mixed', 'abc123XYZ!@#');
/// print(parameterBag.getDigits('mixed')); // Outputs: '123'
String getDigits(String key, [String defaultValue = '']) {
return getString(key, defaultValue).replaceAll(RegExp(r'[^0-9]'), '');
}
/// Returns the parameter value as a string.
///
/// This method retrieves the value associated with the given [key] and converts it to a string.
/// If the key doesn't exist, it returns the [defaultValue].
///
/// Parameters:
/// [key] - The key of the parameter to retrieve.
/// [defaultValue] - Optional. The value to return if the key is not found.
/// Defaults to an empty string.
///
/// Returns:
/// A string representation of the parameter value.
///
/// Throws:
/// [UnexpectedValueException] if the value cannot be converted to a string
/// (i.e., if it's not a String, num, or bool).
///
/// Example:
/// parameterBag.set('number', 42);
/// print(parameterBag.getString('number')); // Outputs: '42'
String getString(String key, [String defaultValue = '']) {
final value = get(key, defaultValue);
if (value is! String && value is! num && value is! bool) {
throw UnexpectedValueException(
'Parameter value "$key" cannot be converted to "String".');
}
return value.toString();
}
/// Returns the parameter value converted to an integer.
///
/// This method retrieves the value associated with the given [key] and attempts to convert it to an integer.
/// If the key doesn't exist or the value cannot be converted to an integer, it returns the [defaultValue].
///
/// Parameters:
/// [key] - The key of the parameter to retrieve.
/// [defaultValue] - Optional. The value to return if the key is not found or the value cannot be converted to an integer.
/// Defaults to 0.
///
/// Returns:
/// An integer representation of the parameter value.
///
/// Example:
/// parameterBag.set('number', '42');
/// print(parameterBag.getInt('number')); // Outputs: 42
/// print(parameterBag.getInt('nonexistent', 10)); // Outputs: 10
int getInt(String key, [int defaultValue = 0]) {
final value = get(key, defaultValue);
if (value is int) return value;
if (value is String) {
return int.tryParse(value) ?? defaultValue;
}
return defaultValue;
}
/// Returns the parameter value converted to a boolean.
///
/// This method retrieves the value associated with the given [key] and attempts to convert it to a boolean.
/// If the key doesn't exist, it returns the [defaultValue].
///
/// The conversion rules are as follows:
/// - If the value is already a boolean, it is returned as-is.
/// - If the value is a string, it returns true if the string is 'true' (case-insensitive) or '1'.
/// - For all other cases, it returns the [defaultValue].
///
/// Parameters:
/// [key] - The key of the parameter to retrieve.
/// [defaultValue] - Optional. The value to return if the key is not found or the value cannot be converted to a boolean.
/// Defaults to false.
///
/// Returns:
/// A boolean representation of the parameter value.
///
/// Example:
/// parameterBag.set('flag1', 'true');
/// parameterBag.set('flag2', '1');
/// parameterBag.set('flag3', 'false');
/// print(parameterBag.getBoolean('flag1')); // Outputs: true
/// print(parameterBag.getBoolean('flag2')); // Outputs: true
/// print(parameterBag.getBoolean('flag3')); // Outputs: false
/// print(parameterBag.getBoolean('nonexistent')); // Outputs: false
bool getBoolean(String key, [bool defaultValue = false]) {
final value = get(key, defaultValue);
if (value is bool) return value;
if (value is String) {
return value.toLowerCase() == 'true' || value == '1';
}
return defaultValue;
}
/// Returns the parameter value converted to an enum.
///
/// This method retrieves the value associated with the given [key] and attempts to convert it
/// to an enum of type [T]. The method compares the string representation of each enum value
/// (without the enum type prefix) to the parameter value.
///
/// Parameters:
/// [key] - The key of the parameter to retrieve.
/// [values] - A list of all possible enum values of type [T].
/// [defaultValue] - Optional. The value to return if the key is not found or the value
/// cannot be converted to an enum. Defaults to null.
///
/// Returns:
/// An enum value of type [T] if a match is found, otherwise returns the [defaultValue].
///
/// Throws:
/// [UnexpectedValueException] if the value exists but cannot be converted to an enum.
///
/// Example:
/// enum Color { red, green, blue }
/// parameterBag.set('color', 'red');
/// var color = parameterBag.getEnum('color', Color.values); // Returns Color.red
T? getEnum<T extends Enum>(String key, List<T> values, [T? defaultValue]) {
final value = get(key);
if (value == null) return defaultValue;
try {
return values.firstWhere((e) => e.toString().split('.').last == value);
} catch (e) {
throw UnexpectedValueException(
'Parameter "$key" cannot be converted to enum: ${e.toString()}');
}
}
/// Filters and transforms a parameter value using a provided function.
///
/// This method retrieves the value associated with the given [key] and applies
/// a [filterFunction] to transform or validate it. If the key doesn't exist,
/// it returns the [defaultValue].
///
/// Parameters:
/// [key] - The key of the parameter to retrieve and filter.
/// [defaultValue] - Optional. The value to return if the key is not found.
/// [filterFunction] - A function that takes the parameter value as input
/// and returns a transformed or validated value.
///
/// Returns:
/// The filtered and transformed parameter value, or the defaultValue if
/// the key is not found.
///
/// Throws:
/// [UnexpectedValueException] if the filterFunction throws an exception,
/// indicating that the parameter value is invalid.
///
/// Example:
/// var age = parameterBag.filter('age',
/// defaultValue: 0,
/// filterFunction: (value) => int.parse(value.toString()));
dynamic filter(String key, {dynamic defaultValue, required String Function(dynamic) filterFunction}) {
final value = get(key, defaultValue);
if (value == null) return defaultValue;
try {
return filterFunction(value);
} catch (e) {
throw UnexpectedValueException('Parameter value "$key" is invalid: ${e.toString()}');
}
}
/// Returns an iterator for the entries in the parameters map.
///
/// This getter provides an iterator that allows for traversing all key-value
/// pairs (entries) in the underlying parameters map. It's particularly useful
/// for iterating over all parameters in the ParameterBag.
///
/// The iterator yields [MapEntry] objects, where each entry contains a String
/// key and a dynamic value, corresponding to a parameter in the ParameterBag.
///
/// This implementation is part of the [Iterable] interface, allowing
/// ParameterBag to be used in for-in loops and with other Iterable methods.
///
/// Returns:
/// An [Iterator] of [MapEntry<String, dynamic>] for the parameters map.
///
/// Example:
/// for (var entry in parameterBag) {
/// print('${entry.key}: ${entry.value}');
/// }
@override
Iterator<MapEntry<String, dynamic>> get iterator => parameters.entries.iterator;
/// Returns the number of parameters in the ParameterBag.
///
/// This getter provides the count of key-value pairs in the underlying
/// parameters map. It's an implementation of the [Countable] interface.
///
/// Returns:
/// An integer representing the number of parameters stored in the ParameterBag.
///
/// Example:
/// int parameterCount = parameterBag.count;
/// print('Number of parameters: $parameterCount');
@override
int get count => parameters.length;
}

View file

@ -9,9 +9,7 @@
* file that was distributed with this source code.
*/
import 'cookie.dart';
import 'header_utils.dart';
import 'header_bag.dart';
import 'package:protevus_http/foundation.dart';
/// ResponseHeaderBag is a container for HTTP response headers.
///