Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ protected AbstractMultipleOpenApiResource(List<GroupedOpenApi> groupedOpenApis,
@Override
public void afterPropertiesSet() {
this.groupedOpenApis.forEach(groupedOpenApi -> {
springDocCustomizers.getGlobalOpenApiCustomizers().ifPresent(groupedOpenApi::addAllOpenApiCustomizer);
springDocCustomizers.getGlobalOperationCustomizers().ifPresent(groupedOpenApi::addAllOperationCustomizer);
groupedOpenApi.addAllOpenApiCustomizer(springDocCustomizers.getGlobalOpenApiCustomizersStream().toList());
groupedOpenApi.addAllOperationCustomizer(springDocCustomizers.getGlobalOperationCustomizersStream().toList());
springDocCustomizers.getGlobalOpenApiMethodFilters().ifPresent(groupedOpenApi::addAllOpenApiMethodFilter);
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -428,8 +428,8 @@ protected OpenAPI getOpenApi(String serverBaseUrl, Locale locale) {
List<Server> servers = openAPI.getServers();
List<Server> serversCopy = cloneViaJson(servers, new TypeReference<List<Server>>() {}, springDocProviders.jsonMapper());

openAPIService.getContext().getBeansOfType(OpenApiLocaleCustomizer.class).values().forEach(openApiLocaleCustomizer -> openApiLocaleCustomizer.customise(openAPI, finalLocale));
springDocCustomizers.getOpenApiCustomizers().ifPresent(apiCustomizers -> apiCustomizers.forEach(openApiCustomizer -> openApiCustomizer.customise(openAPI)));
openAPIService.getContext().getBeanProvider(OpenApiLocaleCustomizer.class).orderedStream().forEach(openApiLocaleCustomizer -> openApiLocaleCustomizer.customise(openAPI, finalLocale));
springDocCustomizers.getOpenApiCustomizersStream().forEach(openApiCustomizer -> openApiCustomizer.customise(openAPI));
if (!CollectionUtils.isEmpty(openAPI.getServers()) && !openAPI.getServers().equals(serversCopy))
openAPIService.setServersPresent(true);

Expand Down Expand Up @@ -863,13 +863,8 @@ else if (ArrayUtils.isEmpty(methodProduces)) {
* @return the router operation
*/
private RouterOperation customizeDataRestRouterOperation(RouterOperation routerOperation) {
Optional<Set<DataRestRouterOperationCustomizer>> optionalDataRestRouterOperationCustomizers = springDocCustomizers.getDataRestRouterOperationCustomizers();
if (optionalDataRestRouterOperationCustomizers.isPresent()) {
Set<DataRestRouterOperationCustomizer> dataRestRouterOperationCustomizerList = optionalDataRestRouterOperationCustomizers.get();
for (DataRestRouterOperationCustomizer dataRestRouterOperationCustomizer : dataRestRouterOperationCustomizerList) {
routerOperation = dataRestRouterOperationCustomizer.customize(routerOperation);
}
}
for (DataRestRouterOperationCustomizer dataRestRouterOperationCustomizer : springDocCustomizers.getDataRestRouterOperationCustomizersStream().toList())
routerOperation = dataRestRouterOperationCustomizer.customize(routerOperation);
return routerOperation;
}

Expand Down Expand Up @@ -1115,15 +1110,11 @@ protected Set<RequestMethod> getDefaultAllowedHttpMethods() {
* @return the operation
*/
protected Operation customizeOperation(Operation operation, Components components, HandlerMethod handlerMethod) {
Optional<Set<OperationCustomizer>> optionalOperationCustomizers = springDocCustomizers.getOperationCustomizers();
if (optionalOperationCustomizers.isPresent()) {
Set<OperationCustomizer> operationCustomizerList = optionalOperationCustomizers.get();
for (OperationCustomizer operationCustomizer : operationCustomizerList) {
if (operationCustomizer instanceof GlobalOperationComponentsCustomizer globalOperationComponentsCustomizer)
operation = globalOperationComponentsCustomizer.customize(operation, components, handlerMethod);
else
operation = operationCustomizer.customize(operation, handlerMethod);
}
for (OperationCustomizer operationCustomizer : springDocCustomizers.getOperationCustomizersStream().toList()) {
if (operationCustomizer instanceof GlobalOperationComponentsCustomizer globalOperationComponentsCustomizer)
operation = globalOperationComponentsCustomizer.customize(operation, components, handlerMethod);
else
operation = operationCustomizer.customize(operation, handlerMethod);
}
return operation;
}
Expand All @@ -1136,13 +1127,8 @@ protected Operation customizeOperation(Operation operation, Components component
* @return the router operation
*/
protected RouterOperation customizeRouterOperation(RouterOperation routerOperation, HandlerMethod handlerMethod) {
Optional<Set<RouterOperationCustomizer>> optionalRouterOperationCustomizers = springDocCustomizers.getRouterOperationCustomizers();
if (optionalRouterOperationCustomizers.isPresent()) {
Set<RouterOperationCustomizer> routerOperationCustomizerList = optionalRouterOperationCustomizers.get();
for (RouterOperationCustomizer routerOperationCustomizer : routerOperationCustomizerList) {
routerOperation = routerOperationCustomizer.customize(routerOperation, handlerMethod);
}
}
for (RouterOperationCustomizer routerOperationCustomizer : springDocCustomizers.getRouterOperationCustomizersStream().toList())
routerOperation = routerOperationCustomizer.customize(routerOperation, handlerMethod);
return routerOperation;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@
*/
package org.springdoc.core.customizers;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;

import org.springdoc.core.filters.GlobalOpenApiMethodFilter;
import org.springdoc.core.filters.OpenApiMethodFilter;
Expand All @@ -38,6 +40,7 @@
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.util.CollectionUtils;

import static org.springdoc.core.utils.Constants.LINKS_SCHEMA_CUSTOMIZER;
Expand Down Expand Up @@ -169,6 +172,15 @@ public Optional<Set<OpenApiCustomizer>> getOpenApiCustomizers() {
return openApiCustomizers;
}

/**
* Gets ordered open api customizer stream.
*
* @return the ordered open api customizer stream
*/
public Stream<OpenApiCustomizer> getOpenApiCustomizersStream() {
return orderedStream(openApiCustomizers);
}

/**
* Gets operation customizers.
*
Expand All @@ -178,6 +190,15 @@ public Optional<Set<OperationCustomizer>> getOperationCustomizers() {
return operationCustomizers;
}

/**
* Gets ordered operation customizer stream.
*
* @return the ordered operation customizer stream
*/
public Stream<OperationCustomizer> getOperationCustomizersStream() {
return orderedStream(operationCustomizers);
}

/**
* Gets router operation customizers.
*
Expand All @@ -187,6 +208,15 @@ public Optional<Set<RouterOperationCustomizer>> getRouterOperationCustomizers()
return routerOperationCustomizers;
}

/**
* Gets ordered router operation customizer stream.
*
* @return the ordered router operation customizer stream
*/
public Stream<RouterOperationCustomizer> getRouterOperationCustomizersStream() {
return orderedStream(routerOperationCustomizers);
}

/**
* Gets data rest router operation customizers.
*
Expand All @@ -196,6 +226,15 @@ public Optional<Set<DataRestRouterOperationCustomizer>> getDataRestRouterOperati
return dataRestRouterOperationCustomizers;
}

/**
* Gets ordered data rest router operation customizer stream.
*
* @return the ordered data rest router operation customizer stream
*/
public Stream<DataRestRouterOperationCustomizer> getDataRestRouterOperationCustomizersStream() {
return orderedStream(dataRestRouterOperationCustomizers);
}

/**
* Gets method filters.
*
Expand All @@ -219,6 +258,15 @@ public Optional<Set<GlobalOpenApiCustomizer>> getGlobalOpenApiCustomizers() {
return globalOpenApiCustomizers;
}

/**
* Gets ordered global open api customizer stream.
*
* @return the ordered global open api customizer stream
*/
public Stream<GlobalOpenApiCustomizer> getGlobalOpenApiCustomizersStream() {
return orderedStream(globalOpenApiCustomizers);
}

/**
* Gets global operation customizers.
*
Expand All @@ -228,6 +276,15 @@ public Optional<Set<GlobalOperationCustomizer>> getGlobalOperationCustomizers()
return globalOperationCustomizers;
}

/**
* Gets ordered global operation customizer stream.
*
* @return the ordered global operation customizer stream
*/
public Stream<GlobalOperationCustomizer> getGlobalOperationCustomizersStream() {
return orderedStream(globalOperationCustomizers);
}

/**
* Gets global open api method filters.
*
Expand Down Expand Up @@ -255,6 +312,13 @@ public Optional<List<ParameterCustomizer>> getParameterCustomizers() {
return parameterCustomizers;
}

private static <T> Stream<T> orderedStream(Optional<? extends Collection<T>> customizers) {
return customizers.stream()
.flatMap(Collection::stream)
.filter(Objects::nonNull)
.sorted(AnnotationAwareOrderComparator.INSTANCE);
}

@Override
public void afterPropertiesSet() {
//add the default customizers
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
*
* *
* * *
* * * *
* * * * * Copyright 2019-2026 the original author or authors.
* * * * *
* * * * * 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
* * * * *
* * * * * https://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 org.springdoc.core.customizers;

import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;

import io.swagger.v3.oas.models.OpenAPI;
import org.junit.jupiter.api.Test;

import org.springframework.core.annotation.Order;

import static org.assertj.core.api.Assertions.assertThat;

class SpringDocCustomizersTest {

@Test
void openApiCustomizersStreamHonorsOrderAnnotation() {
OpenApiCustomizer lastCustomizer = new LastOpenApiCustomizer();
OpenApiCustomizer firstCustomizer = new FirstOpenApiCustomizer();
Set<OpenApiCustomizer> unorderedCustomizers = new LinkedHashSet<>();
unorderedCustomizers.add(lastCustomizer);
unorderedCustomizers.add(firstCustomizer);

SpringDocCustomizers springDocCustomizers = new SpringDocCustomizers(
Optional.of(unorderedCustomizers),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty());

assertThat(springDocCustomizers.getOpenApiCustomizersStream().toList()).containsExactly(firstCustomizer, lastCustomizer);
}

@Test
void globalOpenApiCustomizersStreamHonorsOrderAnnotation() {
GlobalOpenApiCustomizer lastCustomizer = new LastGlobalOpenApiCustomizer();
GlobalOpenApiCustomizer firstCustomizer = new FirstGlobalOpenApiCustomizer();
Set<GlobalOpenApiCustomizer> unorderedCustomizers = new LinkedHashSet<>();
unorderedCustomizers.add(lastCustomizer);
unorderedCustomizers.add(firstCustomizer);

SpringDocCustomizers springDocCustomizers = new SpringDocCustomizers(
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.of(unorderedCustomizers),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty());

assertThat(springDocCustomizers.getGlobalOpenApiCustomizersStream().toList()).containsExactly(firstCustomizer, lastCustomizer);
}

@Order(1)
private static class FirstOpenApiCustomizer implements OpenApiCustomizer {

@Override
public void customise(OpenAPI openApi) {
}
}

@Order(2)
private static class LastOpenApiCustomizer implements OpenApiCustomizer {

@Override
public void customise(OpenAPI openApi) {
}
}

@Order(1)
private static class FirstGlobalOpenApiCustomizer implements GlobalOpenApiCustomizer {

@Override
public void customise(OpenAPI openApi) {
}
}

@Order(2)
private static class LastGlobalOpenApiCustomizer implements GlobalOpenApiCustomizer {

@Override
public void customise(OpenAPI openApi) {
}
}
}