/* * Copyright 2015-2017 Hewlett-Packard Enterprise Development Company, L.P. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. */ package com.hp.autonomy.frontend.find.core.fields; import com.hp.autonomy.frontend.configuration.ConfigService; import com.hp.autonomy.frontend.find.core.configuration.FindConfig; import com.hp.autonomy.frontend.find.core.configuration.UiCustomization; import com.hp.autonomy.searchcomponents.core.fields.FieldsRequest; import com.hp.autonomy.searchcomponents.core.fields.FieldsService; import com.hp.autonomy.searchcomponents.core.fields.TagNameFactory; import com.hp.autonomy.searchcomponents.core.parametricvalues.ParametricRequest; import com.hp.autonomy.searchcomponents.core.parametricvalues.ParametricRequestBuilder; import com.hp.autonomy.searchcomponents.core.parametricvalues.ParametricValuesService; import com.hp.autonomy.searchcomponents.core.search.QueryRestrictions; import com.hp.autonomy.types.requests.idol.actions.tags.FieldPath; import com.hp.autonomy.types.requests.idol.actions.tags.TagName; import com.hp.autonomy.types.requests.idol.actions.tags.ValueDetails; import com.hp.autonomy.types.requests.idol.actions.tags.params.FieldTypeParam; import org.springframework.beans.factory.ObjectFactory; import org.springframework.web.bind.annotation.RequestMapping; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; @RequestMapping(FieldsController.FIELDS_PATH) public abstract class FieldsController<R extends FieldsRequest, E extends Exception, Q extends QueryRestrictions<?>, P extends ParametricRequest<Q>> { public static final String FIELDS_PATH = "/api/public/fields"; public static final String GET_PARAMETRIC_FIELDS_PATH = "/parametric"; public static final String FIELD_TYPES_PARAM = "fieldTypes"; private final FieldsService<R, E> fieldsService; private final ParametricValuesService<P, Q, E> parametricValuesService; private final ObjectFactory<? extends ParametricRequestBuilder<P, Q, ?>> parametricRequestBuilderFactory; private final FieldComparatorFactory fieldComparatorFactory; private final TagNameFactory tagNameFactory; private final ConfigService<? extends FindConfig<?, ?>> configService; @SuppressWarnings("ConstructorWithTooManyParameters") protected FieldsController( final FieldsService<R, E> fieldsService, final ParametricValuesService<P, Q, E> parametricValuesService, final ObjectFactory<? extends ParametricRequestBuilder<P, Q, ?>> parametricRequestBuilderFactory, final FieldComparatorFactory fieldComparatorFactory, final TagNameFactory tagNameFactory, final ConfigService<? extends FindConfig<?, ?>> configService ) { this.fieldsService = fieldsService; this.parametricValuesService = parametricValuesService; this.parametricRequestBuilderFactory = parametricRequestBuilderFactory; this.fieldComparatorFactory = fieldComparatorFactory; this.tagNameFactory = tagNameFactory; this.configService = configService; } /** * Create the query restrictions required to fetch the absolute min and max values for the given field request. */ protected abstract Q createValueDetailsQueryRestrictions(R request); protected List<FieldAndValueDetails> getParametricFields(final R request) throws E { final Predicate<TagName> predicate = alwaysAndNeverShowFilter(); final Map<FieldTypeParam, Set<TagName>> response = fieldsService.getFields(request).entrySet().stream() .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().stream().filter(predicate).collect(Collectors.toSet()))); final TagName autnDateField; if (request.getFieldTypes().contains(FieldTypeParam.NumericDate) && predicate.test(autnDateField = tagNameFactory.buildTagName(ParametricValuesService.AUTN_DATE_FIELD))) { response.compute(FieldTypeParam.NumericDate, (key, maybeValue) -> Optional.ofNullable(maybeValue) .map(value -> { value.add(autnDateField); return value; }) .orElse(Collections.singleton(autnDateField)) ); } final List<FieldAndValueDetails> output = new ArrayList<>(); output.addAll(fetchNumericParametricFieldAndValueDetails(request, FieldTypeParam.NumericDate, response)); output.addAll(fetchNumericParametricFieldAndValueDetails(request, FieldTypeParam.Numeric, response)); output.addAll(fetchParametricFieldAndValueDetails(FieldTypeParam.Parametric, response)); output.sort(fieldComparatorFactory.parametricFieldComparator()); return output; } private Collection<FieldAndValueDetails> fetchParametricFieldAndValueDetails(final FieldTypeParam fieldType, final Map<FieldTypeParam, Set<TagName>> response) throws E { return fetchParametricFieldAndValueDetails(fieldType, response, tagNames -> Collections.emptyMap()); } private Collection<FieldAndValueDetails> fetchNumericParametricFieldAndValueDetails(final R request, final FieldTypeParam fieldType, final Map<FieldTypeParam, Set<TagName>> response) throws E { return fetchParametricFieldAndValueDetails(fieldType, response, tagNames -> { // Fetch the value details for the fields final P parametricRequest = parametricRequestBuilderFactory.getObject() .fieldNames(tagNames.stream() .map(TagName::getId) .collect(Collectors.toList())) .queryRestrictions(createValueDetailsQueryRestrictions(request)) .build(); return parametricValuesService.getValueDetails(parametricRequest); }); } /** * Fetch the parametric fields of the given type along with their min and max values. */ private Collection<FieldAndValueDetails> fetchParametricFieldAndValueDetails(final FieldTypeParam fieldType, final Map<FieldTypeParam, Set<TagName>> response, final ValueDetailsFetch<E> valueDetailsFetch) throws E { if (!response.containsKey(fieldType)) { return Collections.emptyList(); } final Set<TagName> tagNames = response.get(fieldType); tagNames.forEach(tagName -> response.entrySet() .stream() .filter(entry -> entry.getKey() != fieldType) .forEach(entry -> entry.getValue().remove(tagName))); final Map<FieldPath, ValueDetails> valueDetailsResponse = valueDetailsFetch.fetch(tagNames); return tagNames.stream() .map(tagName -> { final FieldAndValueDetails.FieldAndValueDetailsBuilder builder = FieldAndValueDetails.builder() .id(tagName.getId().getNormalisedPath()) .displayName(tagName.getDisplayName()) .type(fieldType); final ValueDetails valueDetails = valueDetailsResponse.get(tagName.getId()); if (valueDetails != null) { builder .max(valueDetails.getMax()) .min(valueDetails.getMin()) .totalValues(valueDetails.getTotalValues()); } return builder.build(); }) .collect(Collectors.toList()); } /** * @return A function which returns true if the TagName matches should be displayed after applying the always and never show lists */ private Predicate<TagName> alwaysAndNeverShowFilter() { final UiCustomization maybeUiCustomization = configService.getConfig().getUiCustomization(); final Collection<FieldPath> parametricAlwaysShow = Optional.ofNullable(maybeUiCustomization) .map(UiCustomization::getParametricAlwaysShow) .orElse(Collections.emptyList()); final Collection<FieldPath> parametricNeverShow = Optional.ofNullable(maybeUiCustomization) .map(UiCustomization::getParametricNeverShow) .orElse(Collections.emptyList()); return tagName -> (parametricAlwaysShow.isEmpty() || parametricAlwaysShow.contains(tagName.getId())) && !parametricNeverShow.contains(tagName.getId()); } @FunctionalInterface private interface ValueDetailsFetch<E extends Exception> { Map<FieldPath, ValueDetails> fetch(final Set<TagName> tagNames) throws E; } }