001/**
002 * Copyright (C) 2006-2025 Talend Inc. - www.talend.com
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.talend.sdk.component.tools.validator;
017
018import static java.util.Collections.emptyList;
019import static java.util.Optional.ofNullable;
020import static java.util.function.Function.identity;
021import static java.util.stream.Collectors.toMap;
022
023import java.util.List;
024import java.util.Map;
025import java.util.Objects;
026import java.util.stream.Stream;
027
028import org.apache.xbean.finder.AnnotationFinder;
029import org.talend.sdk.component.api.configuration.type.DatasetDiscovery;
030import org.talend.sdk.component.api.configuration.type.DatasetDiscoveryConfiguration;
031import org.talend.sdk.component.runtime.manager.ParameterMeta;
032import org.talend.sdk.component.runtime.manager.reflect.ParameterModelService;
033import org.talend.sdk.component.runtime.manager.reflect.parameterenricher.BaseParameterEnricher;
034import org.talend.sdk.component.runtime.manager.service.LocalConfigurationService;
035
036public class DatasetDiscoveryValidator implements Validator {
037
038    private final Validators.ValidatorHelper helper;
039
040    public DatasetDiscoveryValidator(final Validators.ValidatorHelper helper) {
041        this.helper = helper;
042    }
043
044    @Override
045    public Stream<String> validate(final AnnotationFinder finder, final List<Class<?>> components) {
046        final List<Class<?>> datasetDiscoveryClasses = finder.findAnnotatedClasses(DatasetDiscovery.class);
047        final Map<Class<?>, String> datasetDiscoveries = datasetDiscoveryClasses
048                .stream()
049                .collect(toMap(identity(), d -> d.getAnnotation(DatasetDiscovery.class).value()));
050
051        final Stream<String> duplicated = DatasetValidator.duplicatedDataset(datasetDiscoveries.values());
052
053        final Stream<String> i18nError = datasetDiscoveries
054                .entrySet()
055                .stream()
056                .map(entry -> this.helper
057                        .validateFamilyI18nKey(entry.getKey(),
058                                "${family}.datasetdiscovery." + entry.getValue() + "._displayName"))
059                .filter(Objects::nonNull);
060
061        // "cloud" rule - ensure all datasetDiscoveries have a datastore
062        final BaseParameterEnricher.Context context =
063                new BaseParameterEnricher.Context(new LocalConfigurationService(emptyList(), "tools"));
064        final Stream<String> withoutStore = datasetDiscoveryClasses
065                .stream()
066                .map((Class<?> ds) -> this.findDatasetDiscoveryWithoutDataStore(ds, context))
067                .filter(Objects::nonNull)
068                .sorted();
069
070        // A datasetDiscovery must implement interface DatasetDiscoveryConfiguration
071        final Stream<String> implementationError = datasetDiscoveryClasses
072                .stream()
073                .filter((Class<?> ds) -> !(DatasetDiscoveryConfiguration.class.isAssignableFrom(ds)))
074                .map((Class<?> ds) -> "Class " + ds.getName() + " must implement "
075                        + DatasetDiscoveryConfiguration.class)
076                .sorted();
077
078        return Stream
079                .of(duplicated, i18nError, withoutStore, implementationError)
080                .reduce(Stream::concat)
081                .orElseGet(Stream::empty);
082    }
083
084    private String findDatasetDiscoveryWithoutDataStore(final Class<?> ds,
085            final BaseParameterEnricher.Context context) {
086        final List<ParameterMeta> dataset = helper
087                .getParameterModelService()
088                .buildParameterMetas(
089                        Stream.of(new ParameterModelService.Param(ds, ds.getAnnotations(), "datasetDiscovery")), ds,
090                        ofNullable(ds.getPackage()).map(Package::getName).orElse(""), true, context);
091        if (DatasetValidator
092                .flatten(dataset)
093                .noneMatch(prop -> "datastore".equals(prop.getMetadata().get("tcomp::configurationtype::type")))) {
094            return "The datasetDiscovery " + ds.getName()
095                    + " is missing a datastore reference in its configuration (see @DataStore)";
096        }
097        return null;
098    }
099}