Search in sources :

Example 1 with UriMatchVariable

use of io.micronaut.http.uri.UriMatchVariable in project micronaut-core by micronaut-projects.

the class QueryValueArgumentBinder method bind.

/**
 * Binds the argument with {@link QueryValue} annotation to the request
 * (Also binds without annotation if request body is not permitted).
 *
 * It will first try to convert to ConvertibleMultiValues type and if conversion is successful, add the
 * corresponding parameters to the request. (By default the conversion will be successful if the {@link Format}
 * annotation is present and has one of the supported values - see
 * {@link io.micronaut.core.convert.converters.MultiValuesConverterFactory} for specific converters). Otherwise
 * the uri template will be used to deduce what will be done with the request. For example, simple parameters are
 * converted to {@link String}
 */
@Override
public BindingResult<T> bind(ArgumentConversionContext<T> context, HttpRequest<?> source) {
    ConvertibleMultiValues<String> parameters = source.getParameters();
    Argument<T> argument = context.getArgument();
    AnnotationMetadata annotationMetadata = argument.getAnnotationMetadata();
    boolean hasAnnotation = annotationMetadata.hasAnnotation(QueryValue.class);
    HttpMethod httpMethod = source.getMethod();
    boolean permitsRequestBody = HttpMethod.permitsRequestBody(httpMethod);
    BindingResult<T> result;
    if (hasAnnotation || !permitsRequestBody) {
        // First try converting from the ConvertibleMultiValues type and if conversion is successful, return it.
        // Otherwise use the given uri template to deduce what to do with the variable
        Optional<T> multiValueConversion;
        if (annotationMetadata.hasAnnotation(Format.class)) {
            multiValueConversion = conversionService.convert(parameters, context);
        } else {
            multiValueConversion = Optional.empty();
        }
        if (multiValueConversion.isPresent()) {
            result = () -> multiValueConversion;
        } else {
            String parameterName = annotationMetadata.stringValue(QueryValue.class).orElse(argument.getName());
            // If we need to bind all request params to command object
            // checks if the variable is defined with modifier char *, eg. ?pojo*
            boolean bindAll = source.getAttribute(HttpAttributes.ROUTE_MATCH, UriMatchInfo.class).map(umi -> {
                UriMatchVariable uriMatchVariable = umi.getVariableMap().get(parameterName);
                return uriMatchVariable != null && uriMatchVariable.isExploded();
            }).orElse(false);
            if (bindAll) {
                Object value;
                // Only maps and POJOs will "bindAll", lists work like normal
                if (Iterable.class.isAssignableFrom(argument.getType())) {
                    value = doResolve(context, parameters, parameterName);
                    if (value == null) {
                        value = Collections.emptyList();
                    }
                } else {
                    value = parameters.asMap();
                }
                result = doConvert(value, context);
            } else {
                result = doBind(context, parameters, parameterName);
            }
        }
    } else {
        result = BindingResult.EMPTY;
    }
    return result;
}
Also used : ConvertibleMultiValues(io.micronaut.core.convert.value.ConvertibleMultiValues) HttpMethod(io.micronaut.http.HttpMethod) UriMatchVariable(io.micronaut.http.uri.UriMatchVariable) ArgumentConversionContext(io.micronaut.core.convert.ArgumentConversionContext) QueryValue(io.micronaut.http.annotation.QueryValue) AbstractAnnotatedArgumentBinder(io.micronaut.core.bind.annotation.AbstractAnnotatedArgumentBinder) Format(io.micronaut.core.convert.format.Format) UriMatchInfo(io.micronaut.http.uri.UriMatchInfo) Optional(java.util.Optional) AnnotationMetadata(io.micronaut.core.annotation.AnnotationMetadata) HttpAttributes(io.micronaut.http.HttpAttributes) Argument(io.micronaut.core.type.Argument) HttpRequest(io.micronaut.http.HttpRequest) ConversionService(io.micronaut.core.convert.ConversionService) Collections(java.util.Collections) QueryValue(io.micronaut.http.annotation.QueryValue) AnnotationMetadata(io.micronaut.core.annotation.AnnotationMetadata) UriMatchVariable(io.micronaut.http.uri.UriMatchVariable) HttpMethod(io.micronaut.http.HttpMethod)

Example 2 with UriMatchVariable

use of io.micronaut.http.uri.UriMatchVariable in project micronaut-core by micronaut-projects.

the class QueryValueClientArgumentRequestBinder method bind.

/**
 * If value can be converted to ConvertibleMultiValues, then use it and add it to the uriContext.queryParameters.
 * The ConvertibleMultiValues converters are found in
 * {@link io.micronaut.core.convert.converters.MultiValuesConverterFactory} and perform conversion only when the
 * {@link io.micronaut.core.convert.format.Format} annotation has one of the supported values.
 * Otherwise if the {@link io.micronaut.core.convert.format.Format} annotation is present, it is converted to {@link String}. If none of these
 * are satisfied, the{@link io.micronaut.http.uri.UriTemplate} decides what to do with the given value which
 * is supplied as an Object (it is added to uriContext.pathParameter).
 *
 * <br> By default value is converted to ConvertibleMultiValues when the {@link io.micronaut.core.convert.format.Format} annotation is present and has
 * one of the defined above formats. Otherwise empty optional is returned.
 *
 * <br> The default {@link io.micronaut.http.uri.UriTemplate} will convert the value to String and to parameters.
 * Optionally, the value can be formatted if the path template states so.
 */
@Override
public void bind(@NonNull ArgumentConversionContext<Object> context, @NonNull ClientRequestUriContext uriContext, @NonNull Object value, @NonNull MutableHttpRequest<?> request) {
    String parameterName = context.getAnnotationMetadata().stringValue(QueryValue.class).filter(StringUtils::isNotEmpty).orElse(context.getArgument().getName());
    final UriMatchVariable uriVariable = uriContext.getUriTemplate().getVariables().stream().filter(v -> v.getName().equals(parameterName)).findFirst().orElse(null);
    if (uriVariable != null) {
        if (uriVariable.isExploded()) {
            uriContext.setPathParameter(parameterName, value);
        } else {
            String convertedValue = conversionService.convert(value, ConversionContext.STRING.with(context.getAnnotationMetadata())).filter(StringUtils::isNotEmpty).orElse(null);
            if (convertedValue != null) {
                uriContext.setPathParameter(parameterName, convertedValue);
            } else {
                uriContext.setPathParameter(parameterName, value);
            }
        }
    } else {
        ArgumentConversionContext<ConvertibleMultiValues> conversionContext = context.with(Argument.of(ConvertibleMultiValues.class, context.getArgument().getName(), context.getAnnotationMetadata()));
        final Optional<ConvertibleMultiValues<String>> multiValues = conversionService.convert(value, conversionContext).map(values -> (ConvertibleMultiValues<String>) values);
        if (multiValues.isPresent()) {
            Map<String, List<String>> queryParameters = uriContext.getQueryParameters();
            // Add all the parameters
            multiValues.get().forEach((k, v) -> {
                if (queryParameters.containsKey(k)) {
                    queryParameters.get(k).addAll(v);
                } else {
                    queryParameters.put(k, v);
                }
            });
        } else {
            conversionService.convert(value, ConversionContext.STRING.with(context.getAnnotationMetadata())).ifPresent(v -> uriContext.addQueryParameter(parameterName, v));
        }
    }
}
Also used : ConvertibleMultiValues(io.micronaut.core.convert.value.ConvertibleMultiValues) List(java.util.List) UriMatchVariable(io.micronaut.http.uri.UriMatchVariable)

Example 3 with UriMatchVariable

use of io.micronaut.http.uri.UriMatchVariable in project micronaut-core by micronaut-projects.

the class PathVariableAnnotationBinder method bind.

@Override
public BindingResult<T> bind(ArgumentConversionContext<T> context, HttpRequest<?> source) {
    ConvertibleMultiValues<String> parameters = source.getParameters();
    Argument<T> argument = context.getArgument();
    AnnotationMetadata annotationMetadata = argument.getAnnotationMetadata();
    boolean hasAnnotation = annotationMetadata.hasAnnotation(PathVariable.class);
    String parameterName = annotationMetadata.stringValue(PathVariable.class).orElse(argument.getName());
    // If we need to bind all request params to command object
    // checks if the variable is defined with modifier char *
    // eg. ?pojo*
    final Optional<UriMatchInfo> matchInfo = source.getAttribute(HttpAttributes.ROUTE_MATCH, UriMatchInfo.class);
    boolean bindAll = matchInfo.flatMap(umi -> umi.getVariables().stream().filter(v -> v.getName().equals(parameterName)).findFirst().map(UriMatchVariable::isExploded)).orElse(false);
    BindingResult<T> result;
    // be manipulated to override POST or JSON variables
    if (hasAnnotation && matchInfo.isPresent()) {
        final ConvertibleValues<Object> variableValues = ConvertibleValues.of(matchInfo.get().getVariableValues());
        if (bindAll) {
            Object value;
            // Only maps and POJOs will "bindAll", lists work like normal
            if (Iterable.class.isAssignableFrom(argument.getType())) {
                value = doResolve(context, variableValues, parameterName);
                if (value == null) {
                    value = Collections.emptyList();
                }
            } else {
                value = parameters.asMap();
            }
            result = doConvert(value, context);
        } else {
            result = doBind(context, variableValues, parameterName);
        }
    } else {
        // noinspection unchecked
        result = BindingResult.EMPTY;
    }
    return result;
}
Also used : ConvertibleMultiValues(io.micronaut.core.convert.value.ConvertibleMultiValues) ConvertibleValues(io.micronaut.core.convert.value.ConvertibleValues) UriMatchVariable(io.micronaut.http.uri.UriMatchVariable) ArgumentConversionContext(io.micronaut.core.convert.ArgumentConversionContext) AbstractAnnotatedArgumentBinder(io.micronaut.core.bind.annotation.AbstractAnnotatedArgumentBinder) PathVariable(io.micronaut.http.annotation.PathVariable) UriMatchInfo(io.micronaut.http.uri.UriMatchInfo) Optional(java.util.Optional) AnnotationMetadata(io.micronaut.core.annotation.AnnotationMetadata) HttpAttributes(io.micronaut.http.HttpAttributes) Argument(io.micronaut.core.type.Argument) HttpRequest(io.micronaut.http.HttpRequest) ConversionService(io.micronaut.core.convert.ConversionService) Collections(java.util.Collections) AnnotationMetadata(io.micronaut.core.annotation.AnnotationMetadata) UriMatchVariable(io.micronaut.http.uri.UriMatchVariable) UriMatchInfo(io.micronaut.http.uri.UriMatchInfo) PathVariable(io.micronaut.http.annotation.PathVariable)

Example 4 with UriMatchVariable

use of io.micronaut.http.uri.UriMatchVariable in project micronaut-core by micronaut-projects.

the class NullableParameterRule method validate.

@Override
public RouteValidationResult validate(List<UriMatchTemplate> templates, ParameterElement[] parameters, MethodElement method) {
    List<String> errorMessages = new ArrayList<>();
    boolean isClient = method.hasAnnotation("io.micronaut.http.client.annotation.Client");
    // Optional variables can be required in clients
    if (!isClient) {
        Map<String, UriMatchVariable> variables = new HashMap<>();
        Set<UriMatchVariable> required = new HashSet<>();
        for (UriMatchTemplate template : templates) {
            for (UriMatchVariable variable : template.getVariables()) {
                if (!variable.isOptional() || variable.isExploded()) {
                    required.add(variable);
                }
                variables.compute(variable.getName(), (key, var) -> {
                    if (var == null) {
                        if (variable.isOptional() && !variable.isExploded()) {
                            return variable;
                        } else {
                            return null;
                        }
                    } else {
                        if (!var.isOptional() || var.isExploded()) {
                            if (variable.isOptional() && !variable.isExploded()) {
                                return variable;
                            } else {
                                return var;
                            }
                        } else {
                            return var;
                        }
                    }
                });
            }
        }
        for (UriMatchVariable variable : required) {
            if (templates.stream().anyMatch(t -> !t.getVariableNames().contains(variable.getName()))) {
                variables.putIfAbsent(variable.getName(), variable);
            }
        }
        for (UriMatchVariable variable : variables.values()) {
            Arrays.stream(parameters).flatMap(p -> getTypedElements(p).stream()).filter(p -> p.getName().equals(variable.getName())).forEach(p -> {
                ClassElement type = p.getType();
                boolean hasDefaultValue = p.findAnnotation(Bindable.class).flatMap(av -> av.stringValue("defaultValue")).isPresent();
                if (!isNullable(p) && type != null && !type.isAssignable(Optional.class) && !hasDefaultValue) {
                    errorMessages.add(String.format("The uri variable [%s] is optional, but the corresponding method argument [%s %s] is not defined as an Optional or annotated with a Nullable annotation.", variable.getName(), p.getType().toString(), p.getName()));
                }
            });
        }
    }
    return new RouteValidationResult(errorMessages.toArray(new String[0]));
}
Also used : Bindable(io.micronaut.core.bind.annotation.Bindable) Arrays(java.util.Arrays) RouteValidationResult(io.micronaut.validation.routes.RouteValidationResult) UriMatchVariable(io.micronaut.http.uri.UriMatchVariable) Set(java.util.Set) UriMatchTemplate(io.micronaut.http.uri.UriMatchTemplate) ClassElement(io.micronaut.inject.ast.ClassElement) HashMap(java.util.HashMap) Collectors(java.util.stream.Collectors) ParameterElement(io.micronaut.inject.ast.ParameterElement) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) List(java.util.List) TypedElement(io.micronaut.inject.ast.TypedElement) Map(java.util.Map) MethodElement(io.micronaut.inject.ast.MethodElement) Optional(java.util.Optional) NameUtils(io.micronaut.core.naming.NameUtils) Collections(java.util.Collections) Optional(java.util.Optional) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) ClassElement(io.micronaut.inject.ast.ClassElement) UriMatchTemplate(io.micronaut.http.uri.UriMatchTemplate) UriMatchVariable(io.micronaut.http.uri.UriMatchVariable) RouteValidationResult(io.micronaut.validation.routes.RouteValidationResult) HashSet(java.util.HashSet)

Aggregations

UriMatchVariable (io.micronaut.http.uri.UriMatchVariable)4 ConvertibleMultiValues (io.micronaut.core.convert.value.ConvertibleMultiValues)3 Collections (java.util.Collections)3 Optional (java.util.Optional)3 AnnotationMetadata (io.micronaut.core.annotation.AnnotationMetadata)2 AbstractAnnotatedArgumentBinder (io.micronaut.core.bind.annotation.AbstractAnnotatedArgumentBinder)2 ArgumentConversionContext (io.micronaut.core.convert.ArgumentConversionContext)2 ConversionService (io.micronaut.core.convert.ConversionService)2 Argument (io.micronaut.core.type.Argument)2 HttpAttributes (io.micronaut.http.HttpAttributes)2 HttpRequest (io.micronaut.http.HttpRequest)2 UriMatchInfo (io.micronaut.http.uri.UriMatchInfo)2 List (java.util.List)2 Bindable (io.micronaut.core.bind.annotation.Bindable)1 Format (io.micronaut.core.convert.format.Format)1 ConvertibleValues (io.micronaut.core.convert.value.ConvertibleValues)1 NameUtils (io.micronaut.core.naming.NameUtils)1 HttpMethod (io.micronaut.http.HttpMethod)1 PathVariable (io.micronaut.http.annotation.PathVariable)1 QueryValue (io.micronaut.http.annotation.QueryValue)1