resolverFunctionBodyFromElement function

Future<String> resolverFunctionBodyFromElement(
  1. GeneratorCtx ctx,
  2. ExecutableElement element
)

Implementation

Future<String> resolverFunctionBodyFromElement(
  GeneratorCtx ctx,
  ExecutableElement element,
) async {
  bool _hasValidation(Element? element) =>
      element != null && _validateTypeChecker.hasAnnotationOfExact(element);
  bool _isValidation(Element? element) =>
      element != null && _validationTypeChecker.isAssignableFrom(element);

  final validationsInParams = <ParameterElement>[];
  final validations = <String>[];

  String validationErrorMapAddAll(String validation) {
    return 'validationErrorMap.addAll($validation.errorsMap.map((k, v) =>'
        ' MapEntry(k is Enum ? k.name : k.toString(), v))..removeWhere'
        ' ((k, v) => v.isEmpty) );';
  }

  final hasFunctionValidation = _hasValidation(element);
  if (hasFunctionValidation) {
    final firstIndex = element.name.replaceFirstMapped(
      RegExp('[a-zA-Z0-9]'),
      (match) => match.input.substring(match.start, match.end).toUpperCase(),
    );
    final className = '${firstIndex}Args';

    final params = [...element.parameters]
      ..sort((a, b) => _orderForParameter(a) - _orderForParameter(b));

    validations.add(
      'final _validation = $className(${params.map((e) {
        final type = e.type.getDisplayString(withNullability: true);
        final getter =
            isReqCtx(e.type) ? 'ctx' : '(args["${e.name}"] as $type)';
        return '${e.isNamed ? '${e.name}:' : ''}$getter';
      }).join(',')}).validate();'
      '${validationErrorMapAddAll('_validation')}',
    );
  }
  bool makeGlobalValidation = hasFunctionValidation;
  final params = <String>[];
  for (final e in element.parameters) {
    final argName = e.name;
    if (isReqCtx(e.type)) {
      const value = 'ctx';
      params.add(e.isPositional ? value : '${argName}:$value');
    } else {
      final type = e.type.getDisplayString(withNullability: true);
      final typeName = e.type.getDisplayString(withNullability: false);
      final argInfo = argInfoFromElement(e);
      final value =
          argInfo.inline ? '${argName}Arg' : '(args["${argName}"] as $type)';
      if (argInfo.inline) {
        // TODO: 2G support generics
        validations.add(
          'final $value = '
          '${ReCase(typeName).camelCase}$serializerSuffix'
          '.fromJson(ctx.executionCtx.schema.serdeCtx, args);',
        );
      }
      if (_isValidation(e.type.element)) {
        // TODO: 2G pass validation resot in param (don't throw on validation errorĪ€)
        validationsInParams.add(e);
      }

      params.add(e.isPositional ? value : '${argName}:$value');

      if (!hasFunctionValidation && _hasValidation(e.type.element)) {
        makeGlobalValidation = true;
        final resultName = '${argName}ValidationResult';
        final _addToMap = argInfo.inline
            ? validationErrorMapAddAll(resultName)
            : "validationErrorMap['${argName}'] = [$resultName.toError(property: '${argName}')!];";
        validations.add('''
if ($value != null) {
  final $resultName = ${typeName}Validation.fromValue($value as $typeName);
  if ($resultName.hasErrors) {
    $_addToMap
  }
}
''');
      }
    }
  }
  if (makeGlobalValidation) {
    validations.insert(0, '''
final validationErrorMap = <String, List<ValidaError>>{};
''');
    validations.add('''
if (validationErrorMap.isNotEmpty) {
  throw GraphQLError(
    'Input validation error',
    extensions: {
      'validaErrors': validationErrorMap,
    },
    sourceError: validationErrorMap,
  );
}
''');
  }

  final _call = '${element.name}(${params.join(',')})';

  final classResolver = await getClassResolver(ctx, element);
  final handlingFuture =
      element is MethodElement && classResolver?.instantiateCode != null;

  String _getter = element is MethodElement
      ? (classResolver?.instantiateCode ?? 'obj.')
      : '';
  if (handlingFuture) {
    final _resolverClassName = element.enclosingElement.name;
    _getter = 'final _call = ($_resolverClassName r) => r.$_call;\n'
        ' final FutureOr<$_resolverClassName> _obj = \n// ignore: unnecessary_non_null_assertion\n$_getter;'
        ' if (_obj is Future<$_resolverClassName>) return _obj.then(_call);'
        ' else return _call(_obj);';
  }
  // if (handlingFuture) {
  //   _getter = 'return Future.value($_getter).then('
  //       ' (${element.enclosingElement.name} r) => r.$_call);';
  // }

  return '''
final args = ctx.args;
${validations.join('\n')}
${handlingFuture ? _getter : 'return $_getter$_call;'}
''';
}