/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.runtime.module.extension.internal.metadata;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
import static org.mule.runtime.api.metadata.resolving.FailureCode.NO_DYNAMIC_TYPE_AVAILABLE;
import static org.mule.runtime.api.metadata.resolving.MetadataFailure.Builder.newFailure;
import static org.mule.runtime.api.metadata.resolving.MetadataResult.failure;
import static org.mule.runtime.api.metadata.resolving.MetadataResult.success;
import org.mule.metadata.api.model.MetadataType;
import org.mule.runtime.api.meta.model.ComponentModel;
import org.mule.runtime.api.meta.model.parameter.ParameterModel;
import org.mule.runtime.api.metadata.MetadataContext;
import org.mule.runtime.api.metadata.MetadataKey;
import org.mule.runtime.api.metadata.descriptor.InputMetadataDescriptor;
import org.mule.runtime.api.metadata.descriptor.ParameterMetadataDescriptor;
import org.mule.runtime.api.metadata.descriptor.ParameterMetadataDescriptor.ParameterMetadataDescriptorBuilder;
import org.mule.runtime.api.metadata.descriptor.TypeMetadataDescriptor;
import org.mule.runtime.api.metadata.resolving.InputTypeResolver;
import org.mule.runtime.api.metadata.resolving.MetadataFailure;
import org.mule.runtime.api.metadata.resolving.MetadataResult;
import org.mule.runtime.api.metadata.resolving.NamedTypeResolver;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
/**
* Metadata service delegate implementations that handles the resolution
* of a {@link ComponentModel} {@link InputMetadataDescriptor}
*
* @since 4.0
*/
class MetadataInputDelegate extends BaseMetadataDelegate {
MetadataInputDelegate(ComponentModel componentModel) {
super(componentModel);
}
/**
* For each of the Component's {@link ParameterModel} creates the corresponding {@link TypeMetadataDescriptor} using only its
* static {@link MetadataType} and ignoring if any parameter has a dynamic type.
*
* @return A {@link List} containing a {@link MetadataResult} of {@link TypeMetadataDescriptor} for each input parameter using
* only its static {@link MetadataType} and ignoring if any parameter has a dynamic type.
*/
MetadataResult<InputMetadataDescriptor> getInputMetadataDescriptors(MetadataContext context, Object key) {
InputMetadataDescriptor.InputMetadataDescriptorBuilder input = InputMetadataDescriptor.builder();
List<MetadataResult<ParameterMetadataDescriptor>> results = new LinkedList<>();
for (ParameterModel parameter : component.getAllParameterModels()) {
MetadataResult<ParameterMetadataDescriptor> result = getParameterMetadataDescriptor(parameter, context, key);
input.withParameter(parameter.getName(), result.get());
results.add(result);
}
List<MetadataFailure> failures = results.stream().flatMap(e -> e.getFailures().stream()).collect(toList());
return failures.isEmpty() ? success(input.build()) : failure(input.build(), failures);
}
/**
* Given a parameters name, returns the associated {@link NamedTypeResolver}.
*
* @param parameterName name of the parameter
* @return {@link NamedTypeResolver} of the parameter
*/
NamedTypeResolver getParameterResolver(String parameterName) {
return resolverFactory.getInputResolver(parameterName);
}
/**
* Creates a {@link TypeMetadataDescriptor} representing the Component's Content metadata using the
* {@link InputTypeResolver}, if one is available to resolve the {@link MetadataType}. If no the Component has no Content
* parameter, then {@link Optional#empty()} is returned.
*
* @param context current {@link MetadataContext} that will be used by the {@link InputTypeResolver}
* @param key {@link MetadataKey} of the type which's structure has to be resolved
* @return Success with an {@link Optional} {@link TypeMetadataDescriptor} representing the Component's Content metadata,
* resolved using the {@link InputTypeResolver} if one is available to resolve its {@link MetadataType}, returning
* {@link Optional#empty()} if no Content parameter is present Failure if the dynamic resolution fails for any reason.
*/
private MetadataResult<ParameterMetadataDescriptor> getParameterMetadataDescriptor(ParameterModel parameter,
MetadataContext context, Object key) {
ParameterMetadataDescriptorBuilder descriptorBuilder = ParameterMetadataDescriptor.builder(parameter.getName());
if (!parameter.hasDynamicType()) {
return success(descriptorBuilder.withType(parameter.getType()).build());
}
descriptorBuilder.dynamic(true);
MetadataResult<MetadataType> inputMetadataResult = getParameterMetadata(parameter, context, key);
MetadataType type = inputMetadataResult.get() == null ? parameter.getType() : inputMetadataResult.get();
ParameterMetadataDescriptor descriptor = descriptorBuilder.withType(type).build();
return inputMetadataResult.isSuccess() ? success(descriptor) : failure(descriptor, inputMetadataResult.getFailures());
}
/**
* Given a {@link MetadataKey} of a type and a {@link MetadataContext}, resolves the {@link MetadataType} of the
* {@code parameter} using the {@link InputTypeResolver} associated to the current component.
*
* @param context {@link MetadataContext} of the MetaData resolution
* @param key {@link MetadataKey} of the type which's structure has to be resolved
* @return a {@link MetadataResult} with the {@link MetadataType} of the {@code parameter}.
*/
private MetadataResult<MetadataType> getParameterMetadata(ParameterModel parameter, MetadataContext context, Object key) {
try {
boolean allowsNullType = !parameter.isRequired() && (parameter.getDefaultValue() == null);
MetadataType metadata = resolverFactory.getInputResolver(parameter.getName()).getInputMetadata(context, key);
if (isMetadataResolvedCorrectly(metadata, allowsNullType)) {
return success(metadata);
}
MetadataFailure failure = newFailure()
.withMessage(format("Error resolving metadata for the [%s] input parameter", parameter.getName()))
.withFailureCode(NO_DYNAMIC_TYPE_AVAILABLE)
.withReason(NULL_TYPE_ERROR)
.onParameter(parameter.getName());
return failure(parameter.getType(), failure);
} catch (Exception e) {
return failure(parameter.getType(), newFailure(e).onParameter(parameter.getName()));
}
}
}