diff --git a/src/main/java/com/epam/ta/reportportal/core/configs/SpringDocConfiguration.java b/src/main/java/com/epam/ta/reportportal/core/configs/SpringDocConfiguration.java index 15c0a36e43..675a132104 100644 --- a/src/main/java/com/epam/ta/reportportal/core/configs/SpringDocConfiguration.java +++ b/src/main/java/com/epam/ta/reportportal/core/configs/SpringDocConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 EPAM Systems + * Copyright 2024 EPAM Systems * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,24 +16,42 @@ package com.epam.ta.reportportal.core.configs; +import static com.epam.ta.reportportal.commons.querygen.constant.ProjectCriteriaConstant.CRITERIA_PROJECT_ATTRIBUTE_NAME; + import com.epam.ta.reportportal.commons.ReportPortalUser; +import com.epam.ta.reportportal.commons.querygen.CriteriaHolder; import com.epam.ta.reportportal.commons.querygen.Filter; +import com.epam.ta.reportportal.commons.querygen.FilterTarget; import com.epam.ta.reportportal.commons.querygen.Queryable; +import com.epam.ta.reportportal.core.statistics.StatisticsHelper; +import com.epam.ta.reportportal.entity.item.TestItem; +import com.epam.ta.reportportal.entity.launch.Launch; import com.epam.ta.reportportal.entity.user.UserRole; +import com.epam.ta.reportportal.util.SchemaFactory; +import com.epam.ta.reportportal.ws.resolver.FilterFor; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.info.Contact; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.media.IntegerSchema; import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.media.StringSchema; +import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; import io.swagger.v3.oas.models.servers.Server; import io.swagger.v3.oas.models.tags.Tag; +import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.TreeMap; import java.util.stream.Collectors; import javax.servlet.ServletContext; @@ -45,6 +63,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import org.springframework.core.MethodParameter; +import org.springframework.data.domain.Pageable; import org.springframework.security.core.annotation.AuthenticationPrincipal; /** @@ -56,13 +76,14 @@ public class SpringDocConfiguration { static { SpringDocUtils.getConfig().addAnnotationsToIgnore(AuthenticationPrincipal.class); - SpringDocUtils.getConfig().addRequestWrapperToIgnore(Filter.class, Queryable.class, + SpringDocUtils.getConfig().addRequestWrapperToIgnore(Pageable.class, Queryable.class, ReportPortalUser.class, UserRole.class); - SpringDocUtils.getConfig().replaceWithClass(org.springframework.data.domain.Pageable.class, - org.springdoc.core.converters.models.Pageable.class); SpringDocUtils.getConfig().replaceWithClass(Iterable.class, List.class); } + private static final Set hiddenParams = ImmutableSet.builder() + .add(CRITERIA_PROJECT_ATTRIBUTE_NAME).build(); + @Autowired private ServletContext servletContext; @@ -133,4 +154,83 @@ private String convertMethodNameToTitle(String methodName) { StringBuilder title = new StringBuilder(methodName.replaceAll("([A-Z])", " $1")); return title.substring(0, 1).toUpperCase(Locale.ROOT) + title.substring(1).trim(); } + + @Bean + public OperationCustomizer customizeParameters() { + return (operation, handlerMethod) -> { + for (MethodParameter parameter : handlerMethod.getMethodParameters()) { + Class parameterType = parameter.getParameterType(); + + if (parameterType == Filter.class) { + FilterFor filterClass = parameter.getParameterAnnotation(FilterFor.class); + + List defaultParams = Lists.newArrayList(); + if (filterClass != null && (filterClass.value() == TestItem.class + || filterClass.value() == Launch.class)) { + defaultParams = StatisticsHelper.defaultStatisticsFields() + .map(this::buildFilterParameters) + .collect(Collectors.toList()); + } + + List criteriaList = FilterTarget.findByClass(filterClass.value()) + .getCriteriaHolders(); + List filterParams = criteriaList.stream() + .filter(ch -> !hiddenParams.contains(ch.getFilterCriteria())) + .map(this::buildFilterParameters) + .collect(Collectors.toList()); + filterParams.addAll(defaultParams); + setParameters(operation, filterParams); + } else if (parameterType == Pageable.class) { + setParameters(operation, buildPageParameters()); + } + } + return operation; + }; + } + + private Parameter buildFilterParameters(String parameter) { + return new Parameter() + .in(ParameterIn.QUERY.toString()) + .name("filter.eq." + parameter) + .schema(new IntegerSchema()) + .description("Filters by '" + parameter + "'"); + } + + private Parameter buildFilterParameters(CriteriaHolder criteriaHolder) { + Schema schema = SchemaFactory.createSchemaForType(criteriaHolder.getDataType()); + + return new Parameter() + .in(ParameterIn.QUERY.toString()) + .name("filter.eq." + criteriaHolder.getFilterCriteria()) + .schema(schema) + .description("Filters by '" + criteriaHolder.getFilterCriteria() + "'"); + } + + private List buildPageParameters() { + List pageParams = new ArrayList<>(); + pageParams.add(new Parameter() + .in(ParameterIn.QUERY.toString()) + .name("page.page") + .schema(new IntegerSchema()) + .description("Results page you want to retrieve (0..N)")); + pageParams.add(new Parameter() + .in(ParameterIn.QUERY.toString()) + .name("page.size") + .schema(new IntegerSchema()) + .description("Number of records per page")); + pageParams.add(new Parameter() + .in(ParameterIn.QUERY.toString()) + .name("page.sort") + .schema(new StringSchema()) + .description("Sorting criteria in the format: property, (asc|desc). " + + "Default sort order is ascending. " + + "Multiple sort criteria are supported.")); + return pageParams; + } + + private void setParameters(Operation operation, List parameters) { + for (Parameter parameter : parameters) { + operation.addParametersItem(parameter); + } + } } diff --git a/src/main/java/com/epam/ta/reportportal/util/SchemaFactory.java b/src/main/java/com/epam/ta/reportportal/util/SchemaFactory.java new file mode 100644 index 0000000000..7447b17ff7 --- /dev/null +++ b/src/main/java/com/epam/ta/reportportal/util/SchemaFactory.java @@ -0,0 +1,51 @@ +/* + * Copyright 2024 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.ta.reportportal.util; + + +import io.swagger.v3.oas.models.media.*; +import java.sql.Timestamp; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; + + +/** + * @author Andrei Piankouski + */ +public class SchemaFactory { + + private static final Map, Supplier> schemas = new HashMap<>(); + + static { + schemas.put(String.class, StringSchema::new); + schemas.put(Integer.class, IntegerSchema::new); + schemas.put(Long.class, IntegerSchema::new); + schemas.put(Boolean.class, BooleanSchema::new); + schemas.put(Timestamp.class, DateSchema::new); + schemas.put(Date.class, DateSchema::new); + schemas.put(List.class, ArraySchema::new); + } + + public static Schema createSchemaForType(Class type) { + return Optional.ofNullable(schemas.get(type)) + .map(Supplier::get) + .orElse(new StringSchema()); + } +}