2024-09-09 06:08:53 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2024-09-03 20:17:30 +00:00
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
import 'dart:async';
|
2024-09-03 20:17:30 +00:00
|
|
|
import 'package:collection/collection.dart' show IterableExtension;
|
|
|
|
import 'package:protevus_openapi/documentable.dart';
|
|
|
|
import 'package:protevus_auth/auth.dart';
|
|
|
|
import 'package:protevus_http/http.dart';
|
|
|
|
import 'package:protevus_openapi/v3.dart';
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Abstract class representing the runtime of a ResourceController.
|
2024-09-03 20:17:30 +00:00
|
|
|
abstract class ResourceControllerRuntime {
|
2024-09-09 06:08:53 +00:00
|
|
|
/// List of instance variable parameters.
|
2024-09-03 20:17:30 +00:00
|
|
|
List<ResourceControllerParameter>? ivarParameters;
|
2024-09-09 06:08:53 +00:00
|
|
|
|
|
|
|
/// List of operations supported by the ResourceController.
|
2024-09-03 20:17:30 +00:00
|
|
|
late List<ResourceControllerOperation> operations;
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Documenter for the ResourceController.
|
2024-09-03 20:17:30 +00:00
|
|
|
ResourceControllerDocumenter? documenter;
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Retrieves the operation runtime for a given method and path variables.
|
|
|
|
///
|
|
|
|
/// [method] The HTTP method.
|
|
|
|
/// [pathVariables] The list of path variables.
|
|
|
|
///
|
|
|
|
/// Returns the matching [ResourceControllerOperation] or null if not found.
|
2024-09-03 20:17:30 +00:00
|
|
|
ResourceControllerOperation? getOperationRuntime(
|
|
|
|
String method,
|
|
|
|
List<String?> pathVariables,
|
|
|
|
) {
|
|
|
|
return operations.firstWhereOrNull(
|
|
|
|
(op) => op.isSuitableForRequest(method, pathVariables),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Applies request properties to the controller.
|
|
|
|
///
|
|
|
|
/// [untypedController] The ResourceController instance.
|
|
|
|
/// [args] The invocation arguments.
|
2024-09-03 20:17:30 +00:00
|
|
|
void applyRequestProperties(
|
|
|
|
ResourceController untypedController,
|
|
|
|
ResourceControllerOperationInvocationArgs args,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Abstract class for documenting a ResourceController.
|
2024-09-03 20:17:30 +00:00
|
|
|
abstract class ResourceControllerDocumenter {
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Documents the components of a ResourceController.
|
|
|
|
///
|
|
|
|
/// [rc] The ResourceController instance.
|
|
|
|
/// [context] The API documentation context.
|
2024-09-03 20:17:30 +00:00
|
|
|
void documentComponents(ResourceController rc, APIDocumentContext context);
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Documents the operation parameters of a ResourceController.
|
|
|
|
///
|
|
|
|
/// [rc] The ResourceController instance.
|
|
|
|
/// [context] The API documentation context.
|
|
|
|
/// [operation] The operation to document.
|
|
|
|
///
|
|
|
|
/// Returns a list of [APIParameter] objects.
|
2024-09-03 20:17:30 +00:00
|
|
|
List<APIParameter> documentOperationParameters(
|
|
|
|
ResourceController rc,
|
|
|
|
APIDocumentContext context,
|
|
|
|
Operation? operation,
|
|
|
|
);
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Documents the operation request body of a ResourceController.
|
|
|
|
///
|
|
|
|
/// [rc] The ResourceController instance.
|
|
|
|
/// [context] The API documentation context.
|
|
|
|
/// [operation] The operation to document.
|
|
|
|
///
|
|
|
|
/// Returns an [APIRequestBody] object or null.
|
2024-09-03 20:17:30 +00:00
|
|
|
APIRequestBody? documentOperationRequestBody(
|
|
|
|
ResourceController rc,
|
|
|
|
APIDocumentContext context,
|
|
|
|
Operation? operation,
|
|
|
|
);
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Documents the operations of a ResourceController.
|
|
|
|
///
|
|
|
|
/// [rc] The ResourceController instance.
|
|
|
|
/// [context] The API documentation context.
|
|
|
|
/// [route] The route string.
|
|
|
|
/// [path] The API path.
|
|
|
|
///
|
|
|
|
/// Returns a map of operation names to [APIOperation] objects.
|
2024-09-03 20:17:30 +00:00
|
|
|
Map<String, APIOperation> documentOperations(
|
|
|
|
ResourceController rc,
|
|
|
|
APIDocumentContext context,
|
|
|
|
String route,
|
|
|
|
APIPath path,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Represents an operation in a ResourceController.
|
2024-09-03 20:17:30 +00:00
|
|
|
class ResourceControllerOperation {
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Creates a new ResourceControllerOperation.
|
2024-09-03 20:17:30 +00:00
|
|
|
ResourceControllerOperation({
|
|
|
|
required this.scopes,
|
|
|
|
required this.pathVariables,
|
|
|
|
required this.httpMethod,
|
|
|
|
required this.dartMethodName,
|
|
|
|
required this.positionalParameters,
|
|
|
|
required this.namedParameters,
|
|
|
|
required this.invoker,
|
|
|
|
});
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// The required authentication scopes for this operation.
|
2024-09-03 20:17:30 +00:00
|
|
|
final List<AuthScope>? scopes;
|
2024-09-09 06:08:53 +00:00
|
|
|
|
|
|
|
/// The path variables for this operation.
|
2024-09-03 20:17:30 +00:00
|
|
|
final List<String> pathVariables;
|
2024-09-09 06:08:53 +00:00
|
|
|
|
|
|
|
/// The HTTP method for this operation.
|
2024-09-03 20:17:30 +00:00
|
|
|
final String httpMethod;
|
2024-09-09 06:08:53 +00:00
|
|
|
|
|
|
|
/// The name of the Dart method implementing this operation.
|
2024-09-03 20:17:30 +00:00
|
|
|
final String dartMethodName;
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// The positional parameters for this operation.
|
2024-09-03 20:17:30 +00:00
|
|
|
final List<ResourceControllerParameter> positionalParameters;
|
2024-09-09 06:08:53 +00:00
|
|
|
|
|
|
|
/// The named parameters for this operation.
|
2024-09-03 20:17:30 +00:00
|
|
|
final List<ResourceControllerParameter> namedParameters;
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// The function to invoke this operation.
|
2024-09-03 20:17:30 +00:00
|
|
|
final Future<Response> Function(
|
|
|
|
ResourceController resourceController,
|
|
|
|
ResourceControllerOperationInvocationArgs args,
|
|
|
|
) invoker;
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Checks if a request's method and path variables will select this operation.
|
2024-09-03 20:17:30 +00:00
|
|
|
///
|
2024-09-09 06:08:53 +00:00
|
|
|
/// [requestMethod] The HTTP method of the request.
|
|
|
|
/// [requestPathVariables] The path variables of the request.
|
|
|
|
///
|
|
|
|
/// Returns true if the operation is suitable for the request, false otherwise.
|
2024-09-03 20:17:30 +00:00
|
|
|
bool isSuitableForRequest(
|
|
|
|
String? requestMethod,
|
|
|
|
List<String?> requestPathVariables,
|
|
|
|
) {
|
|
|
|
if (requestMethod != null && requestMethod.toUpperCase() != httpMethod) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pathVariables.length != requestPathVariables.length) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return requestPathVariables.every(pathVariables.contains);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Represents a parameter in a ResourceController operation.
|
2024-09-03 20:17:30 +00:00
|
|
|
class ResourceControllerParameter {
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Creates a new ResourceControllerParameter.
|
2024-09-03 20:17:30 +00:00
|
|
|
ResourceControllerParameter({
|
|
|
|
required this.symbolName,
|
|
|
|
required this.name,
|
|
|
|
required this.location,
|
|
|
|
required this.isRequired,
|
|
|
|
required dynamic Function(dynamic input)? decoder,
|
|
|
|
required this.type,
|
|
|
|
required this.defaultValue,
|
|
|
|
required this.acceptFilter,
|
|
|
|
required this.ignoreFilter,
|
|
|
|
required this.requireFilter,
|
|
|
|
required this.rejectFilter,
|
|
|
|
}) : _decoder = decoder;
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Creates a typed ResourceControllerParameter.
|
2024-09-03 20:17:30 +00:00
|
|
|
static ResourceControllerParameter make<T>({
|
|
|
|
required String symbolName,
|
|
|
|
required String? name,
|
|
|
|
required BindingType location,
|
|
|
|
required bool isRequired,
|
|
|
|
required dynamic Function(dynamic input) decoder,
|
|
|
|
required dynamic defaultValue,
|
|
|
|
required List<String>? acceptFilter,
|
|
|
|
required List<String>? ignoreFilter,
|
|
|
|
required List<String>? requireFilter,
|
|
|
|
required List<String>? rejectFilter,
|
|
|
|
}) {
|
|
|
|
return ResourceControllerParameter(
|
|
|
|
symbolName: symbolName,
|
|
|
|
name: name,
|
|
|
|
location: location,
|
|
|
|
isRequired: isRequired,
|
|
|
|
decoder: decoder,
|
|
|
|
type: T,
|
|
|
|
defaultValue: defaultValue,
|
|
|
|
acceptFilter: acceptFilter,
|
|
|
|
ignoreFilter: ignoreFilter,
|
|
|
|
requireFilter: requireFilter,
|
|
|
|
rejectFilter: rejectFilter,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// The name of the symbol in the Dart code.
|
2024-09-03 20:17:30 +00:00
|
|
|
final String symbolName;
|
2024-09-09 06:08:53 +00:00
|
|
|
|
|
|
|
/// The name of the parameter in the API.
|
2024-09-03 20:17:30 +00:00
|
|
|
final String? name;
|
2024-09-09 06:08:53 +00:00
|
|
|
|
|
|
|
/// The type of the parameter.
|
2024-09-03 20:17:30 +00:00
|
|
|
final Type type;
|
2024-09-09 06:08:53 +00:00
|
|
|
|
|
|
|
/// The default value of the parameter.
|
2024-09-03 20:17:30 +00:00
|
|
|
final dynamic defaultValue;
|
2024-09-09 06:08:53 +00:00
|
|
|
|
|
|
|
/// The filter for accepted values.
|
2024-09-03 20:17:30 +00:00
|
|
|
final List<String>? acceptFilter;
|
2024-09-09 06:08:53 +00:00
|
|
|
|
|
|
|
/// The filter for ignored values.
|
2024-09-03 20:17:30 +00:00
|
|
|
final List<String>? ignoreFilter;
|
2024-09-09 06:08:53 +00:00
|
|
|
|
|
|
|
/// The filter for required values.
|
2024-09-03 20:17:30 +00:00
|
|
|
final List<String>? requireFilter;
|
2024-09-09 06:08:53 +00:00
|
|
|
|
|
|
|
/// The filter for rejected values.
|
2024-09-03 20:17:30 +00:00
|
|
|
final List<String>? rejectFilter;
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// The location of the parameter in the request.
|
2024-09-03 20:17:30 +00:00
|
|
|
final BindingType location;
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Indicates if the parameter is required.
|
2024-09-03 20:17:30 +00:00
|
|
|
final bool isRequired;
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// The decoder function for the parameter.
|
2024-09-03 20:17:30 +00:00
|
|
|
final dynamic Function(dynamic input)? _decoder;
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Gets the API parameter location for this parameter.
|
2024-09-03 20:17:30 +00:00
|
|
|
APIParameterLocation get apiLocation {
|
|
|
|
switch (location) {
|
|
|
|
case BindingType.body:
|
|
|
|
throw StateError('body parameters do not have a location');
|
|
|
|
case BindingType.header:
|
|
|
|
return APIParameterLocation.header;
|
|
|
|
case BindingType.query:
|
|
|
|
return APIParameterLocation.query;
|
|
|
|
case BindingType.path:
|
|
|
|
return APIParameterLocation.path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Gets the location name as a string.
|
2024-09-03 20:17:30 +00:00
|
|
|
String get locationName {
|
|
|
|
switch (location) {
|
|
|
|
case BindingType.query:
|
|
|
|
return "query";
|
|
|
|
case BindingType.body:
|
|
|
|
return "body";
|
|
|
|
case BindingType.header:
|
|
|
|
return "header";
|
|
|
|
case BindingType.path:
|
|
|
|
return "path";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Decodes the parameter value from the request.
|
|
|
|
///
|
|
|
|
/// [request] The HTTP request.
|
|
|
|
///
|
|
|
|
/// Returns the decoded value.
|
2024-09-03 20:17:30 +00:00
|
|
|
dynamic decode(Request? request) {
|
|
|
|
switch (location) {
|
|
|
|
case BindingType.query:
|
|
|
|
{
|
|
|
|
final queryParameters = request!.raw.uri.queryParametersAll;
|
|
|
|
final value = request.body.isFormData
|
|
|
|
? request.body.as<Map<String, List<String>>>()[name!]
|
|
|
|
: queryParameters[name!];
|
|
|
|
if (value == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return _decoder!(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
case BindingType.body:
|
|
|
|
{
|
|
|
|
if (request!.body.isEmpty) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return _decoder!(request.body);
|
|
|
|
}
|
|
|
|
case BindingType.header:
|
|
|
|
{
|
|
|
|
final header = request!.raw.headers[name!];
|
|
|
|
if (header == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return _decoder!(header);
|
|
|
|
}
|
|
|
|
|
|
|
|
case BindingType.path:
|
|
|
|
{
|
|
|
|
final path = request!.path.variables[name];
|
|
|
|
if (path == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return _decoder!(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-09 06:08:53 +00:00
|
|
|
/// Holds the arguments for invoking a ResourceController operation.
|
2024-09-03 20:17:30 +00:00
|
|
|
class ResourceControllerOperationInvocationArgs {
|
2024-09-09 06:08:53 +00:00
|
|
|
/// The instance variables for the invocation.
|
2024-09-03 20:17:30 +00:00
|
|
|
late Map<String, dynamic> instanceVariables;
|
2024-09-09 06:08:53 +00:00
|
|
|
|
|
|
|
/// The named arguments for the invocation.
|
2024-09-03 20:17:30 +00:00
|
|
|
late Map<String, dynamic> namedArguments;
|
2024-09-09 06:08:53 +00:00
|
|
|
|
|
|
|
/// The positional arguments for the invocation.
|
2024-09-03 20:17:30 +00:00
|
|
|
late List<dynamic> positionalArguments;
|
|
|
|
}
|