import 'dart:async'; 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'; abstract class ResourceControllerRuntime { List? ivarParameters; late List operations; ResourceControllerDocumenter? documenter; ResourceControllerOperation? getOperationRuntime( String method, List pathVariables, ) { return operations.firstWhereOrNull( (op) => op.isSuitableForRequest(method, pathVariables), ); } void applyRequestProperties( ResourceController untypedController, ResourceControllerOperationInvocationArgs args, ); } abstract class ResourceControllerDocumenter { void documentComponents(ResourceController rc, APIDocumentContext context); List documentOperationParameters( ResourceController rc, APIDocumentContext context, Operation? operation, ); APIRequestBody? documentOperationRequestBody( ResourceController rc, APIDocumentContext context, Operation? operation, ); Map documentOperations( ResourceController rc, APIDocumentContext context, String route, APIPath path, ); } class ResourceControllerOperation { ResourceControllerOperation({ required this.scopes, required this.pathVariables, required this.httpMethod, required this.dartMethodName, required this.positionalParameters, required this.namedParameters, required this.invoker, }); final List? scopes; final List pathVariables; final String httpMethod; final String dartMethodName; final List positionalParameters; final List namedParameters; final Future Function( ResourceController resourceController, ResourceControllerOperationInvocationArgs args, ) invoker; /// Checks if a request's method and path variables will select this binder. /// /// Note that [requestMethod] may be null; if this is the case, only /// path variables are compared. bool isSuitableForRequest( String? requestMethod, List requestPathVariables, ) { if (requestMethod != null && requestMethod.toUpperCase() != httpMethod) { return false; } if (pathVariables.length != requestPathVariables.length) { return false; } return requestPathVariables.every(pathVariables.contains); } } class ResourceControllerParameter { 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; static ResourceControllerParameter make({ required String symbolName, required String? name, required BindingType location, required bool isRequired, required dynamic Function(dynamic input) decoder, required dynamic defaultValue, required List? acceptFilter, required List? ignoreFilter, required List? requireFilter, required List? 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, ); } final String symbolName; final String? name; final Type type; final dynamic defaultValue; final List? acceptFilter; final List? ignoreFilter; final List? requireFilter; final List? rejectFilter; /// The location in the request that this parameter is bound to final BindingType location; final bool isRequired; final dynamic Function(dynamic input)? _decoder; 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; } } 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"; } } dynamic decode(Request? request) { switch (location) { case BindingType.query: { final queryParameters = request!.raw.uri.queryParametersAll; final value = request.body.isFormData ? request.body.as>>()[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); } } } } class ResourceControllerOperationInvocationArgs { late Map instanceVariables; late Map namedArguments; late List positionalArguments; }