M. Justin
M. Justin

Reputation: 21347

How can I exclude a Spring Data REST controller from the generated OpenAPI documentation

I am working with a Spring Data MongoDB project which exports its endpoints via Spring Data REST. It uses the springdoc-openapi-data-rest module of the springdoc-openapi library to generate an OpenAPI interface description for the Spring Data REST endpoints in project.

I have a repository endpoint that I would like to exclude from the generated OpenAPI document.

@Repository
public interface ExcludeMeRepository extends
        PagingAndSortingRepository<ExcludeMe, Integer> {}

If this were a Spring MVC controller being included via springdoc-openapi-ui, I could use the @Hidden annotation from the Swagger Annotations library on the controller. However, this does not appear to work when put on a @Repository class.

@Hidden // Does not appear to have an effect
@Repository
public interface ExcludeMeRepository extends
        PagingAndSortingRepository<ExcludeMe, Integer> {}

I have tried out a few things, but haven't found anything that works that doesn't also feel like a hacky workaround.

How can a respository be excluded from the generated OpenAPI document?

Upvotes: 6

Views: 10818

Answers (3)

M. Justin
M. Justin

Reputation: 21347

The @Hidden annotation is now supported as of the v2.0.0 version of springdoc-openapi. This change was added per springdoc-openapi#1881.

@Hidden // Works as of v2.0.0
@Repository
public interface ExcludeMeRepository extends
        PagingAndSortingRepository<ExcludeMe, Integer> {}

Upvotes: 10

Dusan
Dusan

Reputation: 1

Maybe @RepositoryRestResource(exported = false) is, what you are looking for. For your example, this would look as follows:

@Repository
@RepositoryRestResource(exported = false)
public interface ExcludeMeRepository extends
        PagingAndSortingRepository<ExcludeMe, Integer> {}

See Spring Data REST Reference Guide - Hiding Certain Repositories, Query Methods, or Fields

Upvotes: 0

M. Justin
M. Justin

Reputation: 21347

This is more in the "hacky workaround" category, but I found that using a customized SpringRepositoryRestResourceProvider bean which explicitly excludes the given entity type appears to work. Defining it as a bean overrides the default one in SpringDocDataRestConfiguration.

@Configuration
public class OpenApiConfig {
    @Bean
    public SpringRepositoryRestResourceProvider springRepositoryRestResourceProvider(
            ResourceMappings mappings, ListableBeanFactory listableBeanFactory,
            Associations associations, ApplicationContext applicationContext, 
            DataRestRouterOperationService dataRestRouterOperationService,
            PersistentEntities persistentEntities, ObjectMapper mapper, 
            SpringDocDataRestUtils springDocDataRestUtils) {
        Repositories filteredRepositories = 
                new RepositoriesWithoutExcludeMe(listableBeanFactory);

        return new SpringRepositoryRestResourceProvider(
                mappings, filteredRepositories, associations, 
                applicationContext, dataRestRouterOperationService, 
                persistentEntities, mapper, springDocDataRestUtils);
    }

    private static class RepositoriesWithoutExcludeMe extends Repositories {
        RepositoriesWithoutExcludeMe(ListableBeanFactory factory) {
            super(factory);
        }

        @Override
        public boolean hasRepositoryFor(Class<?> domainClass) {
            return !isExcludeMe(domainClass) 
                   && super.hasRepositoryFor(domainClass);
        }

        @Override
        public Optional<Object> getRepositoryFor(Class<?> domainClass) {
            return optional(domainClass, super::getRepositoryFor);
        }

        @Override
        public <T, S> EntityInformation<T, S> getEntityInformationFor(
                Class<?> domainClass) {
            return nullable(domainClass, super::getEntityInformationFor);
        }

        @Override
        public Optional<RepositoryInformation> getRepositoryInformationFor(
                Class<?> domainClass) {
            return optional(domainClass, super::getRepositoryInformationFor);
        }

        @Override
        public RepositoryInformation getRequiredRepositoryInformation(
                Class<?> domainType) {
            if (isExcludeMe(domainType)) {
                throw new IllegalArgumentException(
                        "No required RepositoryInformation found for domain type " 
                        + domainType.getName());
            } else {
                return nullable(domainType, 
                        super::getRequiredRepositoryInformation);
            }
        }

        @Override
        public Optional<RepositoryInformation> getRepositoryInformation(
                Class<?> repositoryInterface) {
            return optional(repositoryInterface, 
                    super::getRepositoryInformation);
        }

        @Override
        public PersistentEntity<?, ?> getPersistentEntity(
                Class<?> domainClass) {
            return nullable(domainClass, super::getPersistentEntity);
        }

        @Override
        public List<QueryMethod> getQueryMethodsFor(Class<?> domainClass) {
            return isExcludeMe(domainClass) 
                    ? List.of() : super.getQueryMethodsFor(domainClass);
        }

        @Override
        public Iterator<Class<?>> iterator() {
            return Iterators.filter(super.iterator(), c -> !isExcludeMe(c));
        }

        private static boolean isExcludeMe(Class<?> domainClass) {
            return ExcludeMe.class.isAssignableFrom(domainClass);
        }

        private <C extends Class<?>, R> Optional<R> optional(
                C domainClass, Function<C, Optional<R>> function) {
            return isExcludeMe(domainClass) 
                    ? Optional.empty() : function.apply(domainClass);
        }

        private <C extends Class<?>, R> R nullable(
                C domainClass, Function<C, R> function) {
            return isExcludeMe(domainClass) 
                    ? null : function.apply(domainClass);
        }
    }
}

Upvotes: 0

Related Questions